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