1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.telecom;
18
19import android.net.Uri;
20import android.telecom.Connection;
21import android.telecom.ParcelableCall;
22import android.telecom.TelecomManager;
23
24import java.util.ArrayList;
25import java.util.List;
26
27/**
28 * Utilities dealing with {@link ParcelableCall}.
29 */
30public class ParcelableCallUtils {
31    public static class Converter {
32        public ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider,
33                PhoneAccountRegistrar phoneAccountRegistrar) {
34            return ParcelableCallUtils.toParcelableCall(
35                    call, includeVideoProvider, phoneAccountRegistrar);
36        }
37    }
38
39    /**
40     * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
41     *
42     * @param call The {@link Call} to parcel.
43     * @param includeVideoProvider {@code true} if the video provider should be parcelled with the
44     *      {@link Call}, {@code false} otherwise.  Since the {@link ParcelableCall#getVideoCall()}
45     *      method creates a {@link VideoCallImpl} instance on access it is important for the
46     *      recipient of the {@link ParcelableCall} to know if the video provider changed.
47     * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
48     * @return The {@link ParcelableCall} containing all call information from the {@link Call}.
49     */
50    public static ParcelableCall toParcelableCall(
51            Call call,
52            boolean includeVideoProvider,
53            PhoneAccountRegistrar phoneAccountRegistrar) {
54        int state = getParcelableState(call);
55        int capabilities = convertConnectionToCallCapabilities(call.getConnectionCapabilities());
56        int properties = convertConnectionToCallProperties(call.getConnectionProperties());
57        if (call.isConference()) {
58            properties |= android.telecom.Call.Details.PROPERTY_CONFERENCE;
59        }
60
61        if (call.isWorkCall()) {
62            properties |= android.telecom.Call.Details.PROPERTY_ENTERPRISE_CALL;
63        }
64
65        // If this is a single-SIM device, the "default SIM" will always be the only SIM.
66        boolean isDefaultSmsAccount =
67                phoneAccountRegistrar.isUserSelectedSmsPhoneAccount(call.getTargetPhoneAccount());
68        if (call.isRespondViaSmsCapable() && isDefaultSmsAccount) {
69            capabilities |= android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT;
70        }
71
72        if (call.isEmergencyCall()) {
73            capabilities = removeCapability(
74                    capabilities, android.telecom.Call.Details.CAPABILITY_MUTE);
75        }
76
77        if (state == android.telecom.Call.STATE_DIALING) {
78            capabilities = removeCapability(capabilities,
79                    android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
80            capabilities = removeCapability(capabilities,
81                    android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
82        }
83
84        String parentCallId = null;
85        Call parentCall = call.getParentCall();
86        if (parentCall != null) {
87            parentCallId = parentCall.getId();
88        }
89
90        long connectTimeMillis = call.getConnectTimeMillis();
91        List<Call> childCalls = call.getChildCalls();
92        List<String> childCallIds = new ArrayList<>();
93        if (!childCalls.isEmpty()) {
94            long childConnectTimeMillis = Long.MAX_VALUE;
95            for (Call child : childCalls) {
96                if (child.getConnectTimeMillis() > 0) {
97                    childConnectTimeMillis = Math.min(child.getConnectTimeMillis(),
98                            childConnectTimeMillis);
99                }
100                childCallIds.add(child.getId());
101            }
102
103            if (childConnectTimeMillis != Long.MAX_VALUE) {
104                connectTimeMillis = childConnectTimeMillis;
105            }
106        }
107
108        Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
109                call.getHandle() : null;
110        String callerDisplayName = call.getCallerDisplayNamePresentation() ==
111                TelecomManager.PRESENTATION_ALLOWED ?  call.getCallerDisplayName() : null;
112
113        List<Call> conferenceableCalls = call.getConferenceableCalls();
114        List<String> conferenceableCallIds = new ArrayList<String>(conferenceableCalls.size());
115        for (Call otherCall : conferenceableCalls) {
116            conferenceableCallIds.add(otherCall.getId());
117        }
118
119        return new ParcelableCall(
120                call.getId(),
121                state,
122                call.getDisconnectCause(),
123                call.getCannedSmsResponses(),
124                capabilities,
125                properties,
126                connectTimeMillis,
127                handle,
128                call.getHandlePresentation(),
129                callerDisplayName,
130                call.getCallerDisplayNamePresentation(),
131                call.getGatewayInfo(),
132                call.getTargetPhoneAccount(),
133                includeVideoProvider,
134                includeVideoProvider ? call.getVideoProvider() : null,
135                parentCallId,
136                childCallIds,
137                call.getStatusHints(),
138                call.getVideoState(),
139                conferenceableCallIds,
140                call.getIntentExtras(),
141                call.getExtras());
142    }
143
144    private static int getParcelableState(Call call) {
145        int state = CallState.NEW;
146        switch (call.getState()) {
147            case CallState.ABORTED:
148            case CallState.DISCONNECTED:
149                state = android.telecom.Call.STATE_DISCONNECTED;
150                break;
151            case CallState.ACTIVE:
152                state = android.telecom.Call.STATE_ACTIVE;
153                break;
154            case CallState.CONNECTING:
155                state = android.telecom.Call.STATE_CONNECTING;
156                break;
157            case CallState.DIALING:
158                state = android.telecom.Call.STATE_DIALING;
159                break;
160            case CallState.DISCONNECTING:
161                state = android.telecom.Call.STATE_DISCONNECTING;
162                break;
163            case CallState.NEW:
164                state = android.telecom.Call.STATE_NEW;
165                break;
166            case CallState.ON_HOLD:
167                state = android.telecom.Call.STATE_HOLDING;
168                break;
169            case CallState.RINGING:
170                state = android.telecom.Call.STATE_RINGING;
171                break;
172            case CallState.SELECT_PHONE_ACCOUNT:
173                state = android.telecom.Call.STATE_SELECT_PHONE_ACCOUNT;
174                break;
175        }
176
177        // If we are marked as 'locally disconnecting' then mark ourselves as disconnecting instead.
178        // Unless we're disconnect*ED*, in which case leave it at that.
179        if (call.isLocallyDisconnecting() &&
180                (state != android.telecom.Call.STATE_DISCONNECTED)) {
181            state = android.telecom.Call.STATE_DISCONNECTING;
182        }
183        return state;
184    }
185
186    private static final int[] CONNECTION_TO_CALL_CAPABILITY = new int[] {
187        Connection.CAPABILITY_HOLD,
188        android.telecom.Call.Details.CAPABILITY_HOLD,
189
190        Connection.CAPABILITY_SUPPORT_HOLD,
191        android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD,
192
193        Connection.CAPABILITY_MERGE_CONFERENCE,
194        android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE,
195
196        Connection.CAPABILITY_SWAP_CONFERENCE,
197        android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE,
198
199        Connection.CAPABILITY_RESPOND_VIA_TEXT,
200        android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT,
201
202        Connection.CAPABILITY_MUTE,
203        android.telecom.Call.Details.CAPABILITY_MUTE,
204
205        Connection.CAPABILITY_MANAGE_CONFERENCE,
206        android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE,
207
208        Connection.CAPABILITY_SUPPORTS_VT_LOCAL_RX,
209        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_RX,
210
211        Connection.CAPABILITY_SUPPORTS_VT_LOCAL_TX,
212        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX,
213
214        Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
215        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
216
217        Connection.CAPABILITY_SUPPORTS_VT_REMOTE_RX,
218        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX,
219
220        Connection.CAPABILITY_SUPPORTS_VT_REMOTE_TX,
221        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_TX,
222
223        Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
224        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
225
226        Connection.CAPABILITY_SEPARATE_FROM_CONFERENCE,
227        android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE,
228
229        Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
230        android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
231
232        Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
233        android.telecom.Call.Details.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
234
235        Connection.CAPABILITY_CAN_PAUSE_VIDEO,
236        android.telecom.Call.Details.CAPABILITY_CAN_PAUSE_VIDEO,
237
238        Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION,
239        android.telecom.Call.Details.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION,
240
241        Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
242        android.telecom.Call.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
243
244        Connection.CAPABILITY_CAN_PULL_CALL,
245        android.telecom.Call.Details.CAPABILITY_CAN_PULL_CALL
246    };
247
248    private static int convertConnectionToCallCapabilities(int connectionCapabilities) {
249        int callCapabilities = 0;
250        for (int i = 0; i < CONNECTION_TO_CALL_CAPABILITY.length; i += 2) {
251            if ((CONNECTION_TO_CALL_CAPABILITY[i] & connectionCapabilities) ==
252                    CONNECTION_TO_CALL_CAPABILITY[i]) {
253
254                callCapabilities |= CONNECTION_TO_CALL_CAPABILITY[i + 1];
255            }
256        }
257        return callCapabilities;
258    }
259
260    private static final int[] CONNECTION_TO_CALL_PROPERTIES = new int[] {
261        Connection.PROPERTY_HIGH_DEF_AUDIO,
262        android.telecom.Call.Details.PROPERTY_HIGH_DEF_AUDIO,
263
264        Connection.PROPERTY_WIFI,
265        android.telecom.Call.Details.PROPERTY_WIFI,
266
267        Connection.PROPERTY_GENERIC_CONFERENCE,
268        android.telecom.Call.Details.PROPERTY_GENERIC_CONFERENCE,
269
270        Connection.PROPERTY_SHOW_CALLBACK_NUMBER,
271        android.telecom.Call.Details.PROPERTY_EMERGENCY_CALLBACK_MODE,
272
273        Connection.PROPERTY_IS_EXTERNAL_CALL,
274        android.telecom.Call.Details.PROPERTY_IS_EXTERNAL_CALL
275    };
276
277    private static int convertConnectionToCallProperties(int connectionProperties) {
278        int callProperties = 0;
279        for (int i = 0; i < CONNECTION_TO_CALL_PROPERTIES.length; i += 2) {
280            if ((CONNECTION_TO_CALL_PROPERTIES[i] & connectionProperties) ==
281                    CONNECTION_TO_CALL_PROPERTIES[i]) {
282
283                callProperties |= CONNECTION_TO_CALL_PROPERTIES[i + 1];
284            }
285        }
286        return callProperties;
287    }
288
289    /**
290     * Removes the specified capability from the set of capabilities bits and returns the new set.
291     */
292    private static int removeCapability(int capabilities, int capability) {
293        return capabilities & ~capability;
294    }
295
296    private ParcelableCallUtils() {}
297}
298