ParcelableCallUtils.java revision 1e37be5dd86a51b90e461f09dc8a89effe4aee21
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    private static final int CALL_STATE_OVERRIDE_NONE = -1;
32
33    public static class Converter {
34        public ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider,
35                PhoneAccountRegistrar phoneAccountRegistrar) {
36            return ParcelableCallUtils.toParcelableCall(
37                    call, includeVideoProvider, phoneAccountRegistrar, false);
38        }
39    }
40
41    /**
42     * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
43     *
44     * @param call The {@link Call} to parcel.
45     * @param includeVideoProvider {@code true} if the video provider should be parcelled with the
46     *      {@link Call}, {@code false} otherwise.  Since the {@link ParcelableCall#getVideoCall()}
47     *      method creates a {@link VideoCallImpl} instance on access it is important for the
48     *      recipient of the {@link ParcelableCall} to know if the video provider changed.
49     * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
50     * @param supportsExternalCalls Indicates whether the call should be parcelled for an
51     *      {@link InCallService} which supports external calls or not.
52     */
53    public static ParcelableCall toParcelableCall(
54            Call call,
55            boolean includeVideoProvider,
56            PhoneAccountRegistrar phoneAccountRegistrar,
57            boolean supportsExternalCalls) {
58        return toParcelableCall(call, includeVideoProvider, phoneAccountRegistrar,
59                supportsExternalCalls, CALL_STATE_OVERRIDE_NONE /* overrideState */);
60    }
61
62    /**
63     * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
64     *
65     * @param call The {@link Call} to parcel.
66     * @param includeVideoProvider {@code true} if the video provider should be parcelled with the
67     *      {@link Call}, {@code false} otherwise.  Since the {@link ParcelableCall#getVideoCall()}
68     *      method creates a {@link VideoCallImpl} instance on access it is important for the
69     *      recipient of the {@link ParcelableCall} to know if the video provider changed.
70     * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
71     * @param supportsExternalCalls Indicates whether the call should be parcelled for an
72     *      {@link InCallService} which supports external calls or not.
73     * @param overrideState When not {@link #CALL_STATE_OVERRIDE_NONE}, use the provided state as an
74     *      override to whatever is defined in the call.
75     * @return The {@link ParcelableCall} containing all call information from the {@link Call}.
76     */
77    public static ParcelableCall toParcelableCall(
78            Call call,
79            boolean includeVideoProvider,
80            PhoneAccountRegistrar phoneAccountRegistrar,
81            boolean supportsExternalCalls,
82            int overrideState) {
83        int state;
84        if (overrideState == CALL_STATE_OVERRIDE_NONE) {
85            state = getParcelableState(call, supportsExternalCalls);
86        } else {
87            state = overrideState;
88        }
89        int capabilities = convertConnectionToCallCapabilities(call.getConnectionCapabilities());
90        int properties = convertConnectionToCallProperties(call.getConnectionProperties());
91        if (call.isConference()) {
92            properties |= android.telecom.Call.Details.PROPERTY_CONFERENCE;
93        }
94
95        if (call.isWorkCall()) {
96            properties |= android.telecom.Call.Details.PROPERTY_ENTERPRISE_CALL;
97        }
98
99        // If this is a single-SIM device, the "default SIM" will always be the only SIM.
100        boolean isDefaultSmsAccount =
101                phoneAccountRegistrar.isUserSelectedSmsPhoneAccount(call.getTargetPhoneAccount());
102        if (call.isRespondViaSmsCapable() && isDefaultSmsAccount) {
103            capabilities |= android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT;
104        }
105
106        if (call.isEmergencyCall()) {
107            capabilities = removeCapability(
108                    capabilities, android.telecom.Call.Details.CAPABILITY_MUTE);
109        }
110
111        if (state == android.telecom.Call.STATE_DIALING) {
112            capabilities = removeCapability(capabilities,
113                    android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
114            capabilities = removeCapability(capabilities,
115                    android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
116        }
117
118        String parentCallId = null;
119        Call parentCall = call.getParentCall();
120        if (parentCall != null) {
121            parentCallId = parentCall.getId();
122        }
123
124        long connectTimeMillis = call.getConnectTimeMillis();
125        List<Call> childCalls = call.getChildCalls();
126        List<String> childCallIds = new ArrayList<>();
127        if (!childCalls.isEmpty()) {
128            long childConnectTimeMillis = Long.MAX_VALUE;
129            for (Call child : childCalls) {
130                if (child.getConnectTimeMillis() > 0) {
131                    childConnectTimeMillis = Math.min(child.getConnectTimeMillis(),
132                            childConnectTimeMillis);
133                }
134                childCallIds.add(child.getId());
135            }
136
137            if (childConnectTimeMillis != Long.MAX_VALUE) {
138                connectTimeMillis = childConnectTimeMillis;
139            }
140        }
141
142        Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
143                call.getHandle() : null;
144        String callerDisplayName = call.getCallerDisplayNamePresentation() ==
145                TelecomManager.PRESENTATION_ALLOWED ?  call.getCallerDisplayName() : null;
146
147        List<Call> conferenceableCalls = call.getConferenceableCalls();
148        List<String> conferenceableCallIds = new ArrayList<String>(conferenceableCalls.size());
149        for (Call otherCall : conferenceableCalls) {
150            conferenceableCallIds.add(otherCall.getId());
151        }
152
153        return new ParcelableCall(
154                call.getId(),
155                state,
156                call.getDisconnectCause(),
157                call.getCannedSmsResponses(),
158                capabilities,
159                properties,
160                connectTimeMillis,
161                handle,
162                call.getHandlePresentation(),
163                callerDisplayName,
164                call.getCallerDisplayNamePresentation(),
165                call.getGatewayInfo(),
166                call.getTargetPhoneAccount(),
167                includeVideoProvider,
168                includeVideoProvider ? call.getVideoProvider() : null,
169                parentCallId,
170                childCallIds,
171                call.getStatusHints(),
172                call.getVideoState(),
173                conferenceableCallIds,
174                call.getIntentExtras(),
175                call.getExtras());
176    }
177
178    private static int getParcelableState(Call call, boolean supportsExternalCalls) {
179        int state = CallState.NEW;
180        switch (call.getState()) {
181            case CallState.ABORTED:
182            case CallState.DISCONNECTED:
183                state = android.telecom.Call.STATE_DISCONNECTED;
184                break;
185            case CallState.ACTIVE:
186                state = android.telecom.Call.STATE_ACTIVE;
187                break;
188            case CallState.CONNECTING:
189                state = android.telecom.Call.STATE_CONNECTING;
190                break;
191            case CallState.DIALING:
192                state = android.telecom.Call.STATE_DIALING;
193                break;
194            case CallState.PULLING:
195                if (supportsExternalCalls) {
196                    // The InCallService supports external calls, so it must handle
197                    // STATE_PULLING_CALL.
198                    state = android.telecom.Call.STATE_PULLING_CALL;
199                } else {
200                    // The InCallService does NOT support external calls, so remap
201                    // STATE_PULLING_CALL to STATE_DIALING.  In essence, pulling a call can be seen
202                    // as a form of dialing, so it is appropriate for InCallServices which do not
203                    // handle external calls.
204                    state = android.telecom.Call.STATE_DIALING;
205                }
206                break;
207            case CallState.DISCONNECTING:
208                state = android.telecom.Call.STATE_DISCONNECTING;
209                break;
210            case CallState.NEW:
211                state = android.telecom.Call.STATE_NEW;
212                break;
213            case CallState.ON_HOLD:
214                state = android.telecom.Call.STATE_HOLDING;
215                break;
216            case CallState.RINGING:
217                state = android.telecom.Call.STATE_RINGING;
218                break;
219            case CallState.SELECT_PHONE_ACCOUNT:
220                state = android.telecom.Call.STATE_SELECT_PHONE_ACCOUNT;
221                break;
222        }
223
224        // If we are marked as 'locally disconnecting' then mark ourselves as disconnecting instead.
225        // Unless we're disconnect*ED*, in which case leave it at that.
226        if (call.isLocallyDisconnecting() &&
227                (state != android.telecom.Call.STATE_DISCONNECTED)) {
228            state = android.telecom.Call.STATE_DISCONNECTING;
229        }
230        return state;
231    }
232
233    private static final int[] CONNECTION_TO_CALL_CAPABILITY = new int[] {
234        Connection.CAPABILITY_HOLD,
235        android.telecom.Call.Details.CAPABILITY_HOLD,
236
237        Connection.CAPABILITY_SUPPORT_HOLD,
238        android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD,
239
240        Connection.CAPABILITY_MERGE_CONFERENCE,
241        android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE,
242
243        Connection.CAPABILITY_SWAP_CONFERENCE,
244        android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE,
245
246        Connection.CAPABILITY_RESPOND_VIA_TEXT,
247        android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT,
248
249        Connection.CAPABILITY_MUTE,
250        android.telecom.Call.Details.CAPABILITY_MUTE,
251
252        Connection.CAPABILITY_MANAGE_CONFERENCE,
253        android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE,
254
255        Connection.CAPABILITY_SUPPORTS_VT_LOCAL_RX,
256        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_RX,
257
258        Connection.CAPABILITY_SUPPORTS_VT_LOCAL_TX,
259        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX,
260
261        Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
262        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
263
264        Connection.CAPABILITY_SUPPORTS_VT_REMOTE_RX,
265        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX,
266
267        Connection.CAPABILITY_SUPPORTS_VT_REMOTE_TX,
268        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_TX,
269
270        Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
271        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
272
273        Connection.CAPABILITY_SEPARATE_FROM_CONFERENCE,
274        android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE,
275
276        Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
277        android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
278
279        Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
280        android.telecom.Call.Details.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
281
282        Connection.CAPABILITY_CAN_PAUSE_VIDEO,
283        android.telecom.Call.Details.CAPABILITY_CAN_PAUSE_VIDEO,
284
285        Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION,
286        android.telecom.Call.Details.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION,
287
288        Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
289        android.telecom.Call.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
290
291        Connection.CAPABILITY_CAN_PULL_CALL,
292        android.telecom.Call.Details.CAPABILITY_CAN_PULL_CALL
293    };
294
295    private static int convertConnectionToCallCapabilities(int connectionCapabilities) {
296        int callCapabilities = 0;
297        for (int i = 0; i < CONNECTION_TO_CALL_CAPABILITY.length; i += 2) {
298            if ((CONNECTION_TO_CALL_CAPABILITY[i] & connectionCapabilities) ==
299                    CONNECTION_TO_CALL_CAPABILITY[i]) {
300
301                callCapabilities |= CONNECTION_TO_CALL_CAPABILITY[i + 1];
302            }
303        }
304        return callCapabilities;
305    }
306
307    private static final int[] CONNECTION_TO_CALL_PROPERTIES = new int[] {
308        Connection.PROPERTY_HIGH_DEF_AUDIO,
309        android.telecom.Call.Details.PROPERTY_HIGH_DEF_AUDIO,
310
311        Connection.PROPERTY_WIFI,
312        android.telecom.Call.Details.PROPERTY_WIFI,
313
314        Connection.PROPERTY_GENERIC_CONFERENCE,
315        android.telecom.Call.Details.PROPERTY_GENERIC_CONFERENCE,
316
317        Connection.PROPERTY_SHOW_CALLBACK_NUMBER,
318        android.telecom.Call.Details.PROPERTY_EMERGENCY_CALLBACK_MODE,
319
320        Connection.PROPERTY_IS_EXTERNAL_CALL,
321        android.telecom.Call.Details.PROPERTY_IS_EXTERNAL_CALL,
322
323        Connection.PROPERTY_HAS_CDMA_VOICE_PRIVACY,
324        android.telecom.Call.Details.PROPERTY_HAS_CDMA_VOICE_PRIVACY
325    };
326
327    private static int convertConnectionToCallProperties(int connectionProperties) {
328        int callProperties = 0;
329        for (int i = 0; i < CONNECTION_TO_CALL_PROPERTIES.length; i += 2) {
330            if ((CONNECTION_TO_CALL_PROPERTIES[i] & connectionProperties) ==
331                    CONNECTION_TO_CALL_PROPERTIES[i]) {
332
333                callProperties |= CONNECTION_TO_CALL_PROPERTIES[i + 1];
334            }
335        }
336        return callProperties;
337    }
338
339    /**
340     * Removes the specified capability from the set of capabilities bits and returns the new set.
341     */
342    private static int removeCapability(int capabilities, int capability) {
343        return capabilities & ~capability;
344    }
345
346    private ParcelableCallUtils() {}
347}
348