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 
17 package com.android.server.telecom;
18 
19 import static android.telecom.Call.Details.DIRECTION_INCOMING;
20 import static android.telecom.Call.Details.DIRECTION_OUTGOING;
21 import static android.telecom.Call.Details.DIRECTION_UNKNOWN;
22 
23 import android.net.Uri;
24 import android.os.Bundle;
25 import android.telecom.Connection;
26 import android.telecom.DisconnectCause;
27 import android.telecom.ParcelableCall;
28 import android.telecom.ParcelableRttCall;
29 import android.telecom.TelecomManager;
30 import android.text.TextUtils;
31 
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.Iterator;
35 import java.util.List;
36 
37 /**
38  * Utilities dealing with {@link ParcelableCall}.
39  */
40 public class ParcelableCallUtils {
41     private static final int CALL_STATE_OVERRIDE_NONE = -1;
42 
43     /**
44      * A list of extra keys which should be removed from a {@link ParcelableCall} when it is being
45      * generated for the purpose of sending to a incallservice other than the system incallservice.
46      * By convention we only pass keys namespaced with android.*, however there are some keys which
47      * should not be passed to non-system incallservice apps either.
48      */
49     private static List<String> EXTRA_KEYS_TO_SANITIZE;
50     static {
51         EXTRA_KEYS_TO_SANITIZE = new ArrayList<>();
52         EXTRA_KEYS_TO_SANITIZE.add(android.telecom.Connection.EXTRA_SIP_INVITE);
53     }
54 
55     /**
56      * A list of extra keys which should be added to {@link ParcelableCall} when it is being
57      * generated for the purpose of sending to a CallScreeningService which has access to these
58      * restricted keys.
59      */
60     private static List<String> RESTRICTED_CALL_SCREENING_EXTRA_KEYS;
61     static {
62         RESTRICTED_CALL_SCREENING_EXTRA_KEYS = new ArrayList<>();
63         RESTRICTED_CALL_SCREENING_EXTRA_KEYS.add(android.telecom.Connection.EXTRA_SIP_INVITE);
64     }
65 
66     public static class Converter {
toParcelableCall(Call call, boolean includeVideoProvider, PhoneAccountRegistrar phoneAccountRegistrar)67         public ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider,
68                 PhoneAccountRegistrar phoneAccountRegistrar) {
69             return ParcelableCallUtils.toParcelableCall(
70                     call, includeVideoProvider, phoneAccountRegistrar, false, false, false);
71         }
72 
toParcelableCallForScreening(Call call, boolean areRestrictedExtrasIncluded)73         public ParcelableCall toParcelableCallForScreening(Call call,
74                 boolean areRestrictedExtrasIncluded) {
75             return ParcelableCallUtils.toParcelableCallForScreening(call,
76                     areRestrictedExtrasIncluded);
77         }
78     }
79 
80     /**
81      * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
82      *
83      * @param call The {@link Call} to parcel.
84      * @param includeVideoProvider {@code true} if the video provider should be parcelled with the
85      *      {@link Call}, {@code false} otherwise.  Since the {@link ParcelableCall#getVideoCall()}
86      *      method creates a {@link VideoCallImpl} instance on access it is important for the
87      *      recipient of the {@link ParcelableCall} to know if the video provider changed.
88      * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
89      * @param supportsExternalCalls Indicates whether the call should be parcelled for an
90      *      {@link InCallService} which supports external calls or not.
91      * @param includeRttCall {@code true} if the RTT call should be included, {@code false}
92      *      otherwise.
93      * @param isForSystemInCallService {@code true} if this call is being parcelled for the system incallservice,
94      *      {@code false} otherwise.  When parceling for the system incallservice, the entire call extras
95      *      is included.  When parceling for anything other than the system incallservice, some extra key
96      *      values will be stripped for privacy sake.
97      */
toParcelableCall( Call call, boolean includeVideoProvider, PhoneAccountRegistrar phoneAccountRegistrar, boolean supportsExternalCalls, boolean includeRttCall, boolean isForSystemInCallService)98     public static ParcelableCall toParcelableCall(
99             Call call,
100             boolean includeVideoProvider,
101             PhoneAccountRegistrar phoneAccountRegistrar,
102             boolean supportsExternalCalls,
103             boolean includeRttCall,
104             boolean isForSystemInCallService) {
105         return toParcelableCall(call, includeVideoProvider, phoneAccountRegistrar,
106                 supportsExternalCalls, CALL_STATE_OVERRIDE_NONE /* overrideState */,
107                 includeRttCall, isForSystemInCallService);
108     }
109 
110     /**
111      * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
112      *
113      * @param call The {@link Call} to parcel.
114      * @param includeVideoProvider {@code true} if the video provider should be parcelled with the
115      *      {@link Call}, {@code false} otherwise.  Since the {@link ParcelableCall#getVideoCall()}
116      *      method creates a {@link VideoCallImpl} instance on access it is important for the
117      *      recipient of the {@link ParcelableCall} to know if the video provider changed.
118      * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
119      * @param supportsExternalCalls Indicates whether the call should be parcelled for an
120      *      {@link InCallService} which supports external calls or not.
121      * @param overrideState When not {@link #CALL_STATE_OVERRIDE_NONE}, use the provided state as an
122      *      override to whatever is defined in the call.
123      * @param isForSystemInCallService {@code true} if this call is being parcelled for the system incallservice,
124      *      {@code false} otherwise.  When parceling for the system incallservice, the entire call extras
125      *      is included.  When parceling for anything other than the system incallservice, some extra key
126      *      values will be stripped for privacy sake.
127      * @return The {@link ParcelableCall} containing all call information from the {@link Call}.
128      */
toParcelableCall( Call call, boolean includeVideoProvider, PhoneAccountRegistrar phoneAccountRegistrar, boolean supportsExternalCalls, int overrideState, boolean includeRttCall, boolean isForSystemInCallService)129     public static ParcelableCall toParcelableCall(
130             Call call,
131             boolean includeVideoProvider,
132             PhoneAccountRegistrar phoneAccountRegistrar,
133             boolean supportsExternalCalls,
134             int overrideState,
135             boolean includeRttCall,
136             boolean isForSystemInCallService) {
137         int state;
138         if (overrideState == CALL_STATE_OVERRIDE_NONE) {
139             state = getParcelableState(call, supportsExternalCalls);
140         } else {
141             state = overrideState;
142         }
143         int capabilities = convertConnectionToCallCapabilities(call.getConnectionCapabilities());
144         int properties = convertConnectionToCallProperties(call.getConnectionProperties());
145         int supportedAudioRoutes = call.getSupportedAudioRoutes();
146 
147         if (call.isConference()) {
148             properties |= android.telecom.Call.Details.PROPERTY_CONFERENCE;
149         }
150 
151         if (call.isWorkCall()) {
152             properties |= android.telecom.Call.Details.PROPERTY_ENTERPRISE_CALL;
153         }
154 
155         if (call.getIsVoipAudioMode()) {
156             properties |= android.telecom.Call.Details.PROPERTY_VOIP_AUDIO_MODE;
157         }
158 
159         // If this is a single-SIM device, the "default SIM" will always be the only SIM.
160         boolean isDefaultSmsAccount = phoneAccountRegistrar != null &&
161                 phoneAccountRegistrar.isUserSelectedSmsPhoneAccount(call.getTargetPhoneAccount());
162         if (call.isRespondViaSmsCapable() && isDefaultSmsAccount) {
163             capabilities |= android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT;
164         }
165 
166         if (call.isEmergencyCall()) {
167             capabilities = removeCapability(
168                     capabilities, android.telecom.Call.Details.CAPABILITY_MUTE);
169         }
170 
171         if (state == android.telecom.Call.STATE_DIALING) {
172             capabilities = removeCapability(capabilities,
173                     android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
174             capabilities = removeCapability(capabilities,
175                     android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
176         }
177 
178         String parentCallId = null;
179         Call parentCall = call.getParentCall();
180         if (parentCall != null) {
181             parentCallId = parentCall.getId();
182         }
183 
184         List<Call> childCalls = call.getChildCalls();
185         List<String> childCallIds = new ArrayList<>();
186         if (!childCalls.isEmpty()) {
187             for (Call child : childCalls) {
188                 childCallIds.add(child.getId());
189             }
190         }
191 
192         Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
193                 call.getHandle() : null;
194         String callerDisplayName = call.getCallerDisplayNamePresentation() ==
195                 TelecomManager.PRESENTATION_ALLOWED ?  call.getCallerDisplayName() : null;
196 
197         List<Call> conferenceableCalls = call.getConferenceableCalls();
198         List<String> conferenceableCallIds = new ArrayList<String>(conferenceableCalls.size());
199         for (Call otherCall : conferenceableCalls) {
200             conferenceableCallIds.add(otherCall.getId());
201         }
202 
203         ParcelableRttCall rttCall = includeRttCall ? getParcelableRttCall(call) : null;
204         int callDirection;
205         if (call.isIncoming()) {
206             callDirection = DIRECTION_INCOMING;
207         } else if (call.isUnknown()) {
208             callDirection = DIRECTION_UNKNOWN;
209         } else {
210             callDirection = DIRECTION_OUTGOING;
211         }
212 
213         String activeChildCallId = null;
214         if (call.getConferenceLevelActiveCall() != null) {
215             activeChildCallId = call.getConferenceLevelActiveCall().getId();
216         }
217 
218         Bundle extras;
219         if (isForSystemInCallService) {
220             extras = call.getExtras();
221         } else {
222             extras = sanitizeExtras(call.getExtras());
223         }
224 
225         return new ParcelableCall.ParcelableCallBuilder()
226                 .setId(call.getId())
227                 .setState(state)
228                 .setDisconnectCause(call.getDisconnectCause())
229                 .setCannedSmsResponses(call.getCannedSmsResponses())
230                 .setCapabilities(capabilities)
231                 .setProperties(properties)
232                 .setSupportedAudioRoutes(supportedAudioRoutes)
233                 .setConnectTimeMillis(call.getConnectTimeMillis())
234                 .setHandle(handle)
235                 .setHandlePresentation(call.getHandlePresentation())
236                 .setCallerDisplayName(callerDisplayName)
237                 .setCallerDisplayNamePresentation(call.getCallerDisplayNamePresentation())
238                 .setGatewayInfo(call.getGatewayInfo())
239                 .setAccountHandle(call.getTargetPhoneAccount())
240                 .setIsVideoCallProviderChanged(includeVideoProvider)
241                 .setVideoCallProvider(includeVideoProvider ? call.getVideoProvider() : null)
242                 .setIsRttCallChanged(includeRttCall)
243                 .setRttCall(rttCall)
244                 .setParentCallId(parentCallId)
245                 .setChildCallIds(childCallIds)
246                 .setStatusHints(call.getStatusHints())
247                 .setVideoState(call.getVideoState())
248                 .setConferenceableCallIds(conferenceableCallIds)
249                 .setIntentExtras(call.getIntentExtras())
250                 .setExtras(extras)
251                 .setCreationTimeMillis(call.getCreationTimeMillis())
252                 .setCallDirection(callDirection)
253                 .setCallerNumberVerificationStatus(call.getCallerNumberVerificationStatus())
254                 .setContactDisplayName(call.getName())
255                 .setActiveChildCallId(activeChildCallId)
256                 .createParcelableCall();
257     }
258 
259     /**
260      * Creates a ParcelableCall with the bare minimum properties required for a
261      * {@link android.telecom.CallScreeningService}.  We ONLY expose the following:
262      * <ul>
263      *     <li>Call Id (not exposed to public, but needed to associated calls)</li>
264      *     <li>Call directoin</li>
265      *     <li>Creation time</li>
266      *     <li>Connection time</li>
267      *     <li>Handle (phone number)</li>
268      *     <li>Handle (phone number) presentation</li>
269      *     <li>Caller number verification status (verstat)</li>
270      * </ul>
271      * All other fields are nulled or set to 0 values.
272      * Where the call screening service is part of the system incallservice, the
273      * {@link Connection#EXTRA_SIP_INVITE} header information is also sent to the call screening
274      * service (since the system incallservice has access to this anyways).
275      * @param call The telecom call to send to a call screening service.
276      * @param areRestrictedExtrasIncluded {@code true} if the set of restricted extras defined in
277      *                                    {@link #RESTRICTED_CALL_SCREENING_EXTRA_KEYS} are to
278      *                                    be included in the parceled call, {@code false} otherwise.
279      * @return Minimal {@link ParcelableCall} to send to the call screening service.
280      */
toParcelableCallForScreening(Call call, boolean areRestrictedExtrasIncluded)281     public static ParcelableCall toParcelableCallForScreening(Call call,
282             boolean areRestrictedExtrasIncluded) {
283         Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
284                 call.getHandle() : null;
285         int callDirection;
286         if (call.isIncoming()) {
287             callDirection = DIRECTION_INCOMING;
288         } else if (call.isUnknown()) {
289             callDirection = DIRECTION_UNKNOWN;
290         } else {
291             callDirection = DIRECTION_OUTGOING;
292         }
293         Bundle callExtras;
294         if (areRestrictedExtrasIncluded) {
295             callExtras = sanitizeRestrictedCallExtras(call.getExtras());
296         } else {
297             callExtras = new Bundle();
298         }
299 
300         return new ParcelableCall.ParcelableCallBuilder()
301                 .setId(call.getId())
302                 .setState(getParcelableState(call, false /* supportsExternalCalls */))
303                 .setDisconnectCause(new DisconnectCause(DisconnectCause.UNKNOWN))
304                 .setCannedSmsResponses(null)
305                 .setCapabilities(0)
306                 .setProperties(0)
307                 .setSupportedAudioRoutes(0)
308                 .setConnectTimeMillis(call.getConnectTimeMillis())
309                 .setHandle(handle)
310                 .setHandlePresentation(call.getHandlePresentation())
311                 .setCallerDisplayName(null)
312                 .setCallerDisplayNamePresentation(0)
313                 .setGatewayInfo(null)
314                 .setAccountHandle(null)
315                 .setIsVideoCallProviderChanged(false)
316                 .setVideoCallProvider(null)
317                 .setIsRttCallChanged(false)
318                 .setRttCall(null)
319                 .setParentCallId(null)
320                 .setChildCallIds(null)
321                 .setStatusHints(null)
322                 .setVideoState(0)
323                 .setConferenceableCallIds(Collections.emptyList())
324                 .setIntentExtras(null)
325                 .setExtras(callExtras)
326                 .setCreationTimeMillis(call.getCreationTimeMillis())
327                 .setCallDirection(callDirection)
328                 .setCallerNumberVerificationStatus(call.getCallerNumberVerificationStatus())
329                 .setContactDisplayName(null)
330                 .setActiveChildCallId(null)
331                 .createParcelableCall();
332     }
333 
334     /**
335      * Sanitize the extras bundle passed in, removing keys which should not be sent to non-system
336      * incallservice apps.
337      * @param oldExtras Extras bundle to sanitize.
338      * @return The sanitized extras bundle.
339      */
sanitizeExtras(Bundle oldExtras)340     private static Bundle sanitizeExtras(Bundle oldExtras) {
341         if (oldExtras == null) {
342             return new Bundle();
343         }
344         Bundle extras = new Bundle(oldExtras);
345         for (String key : EXTRA_KEYS_TO_SANITIZE) {
346             extras.remove(key);
347         }
348 
349         // As a catch-all remove any that don't start with android namespace.
350         Iterator<String> toCheck = extras.keySet().iterator();
351         while (toCheck.hasNext()) {
352             String extraKey = toCheck.next();
353             if (TextUtils.isEmpty(extraKey) || !extraKey.startsWith("android.")) {
354                 toCheck.remove();
355             }
356         }
357         return extras;
358     }
359 
360     /**
361      * Sanitize the extras bundle passed in, removing keys which should not be sent to call
362      * screening services which have access to the restricted extras.
363      * @param oldExtras Extras bundle to sanitize.
364      * @return The sanitized extras bundle.
365      */
sanitizeRestrictedCallExtras(Bundle oldExtras)366     private static Bundle sanitizeRestrictedCallExtras(Bundle oldExtras) {
367         if (oldExtras == null) {
368             return new Bundle();
369         }
370         Bundle extras = new Bundle(oldExtras);
371         Iterator<String> toCheck = extras.keySet().iterator();
372         while (toCheck.hasNext()) {
373             String extraKey = toCheck.next();
374             if (TextUtils.isEmpty(extraKey)
375                     || !RESTRICTED_CALL_SCREENING_EXTRA_KEYS.contains(extraKey)) {
376                 toCheck.remove();
377             }
378         }
379         return extras;
380     }
381 
getParcelableState(Call call, boolean supportsExternalCalls)382     private static int getParcelableState(Call call, boolean supportsExternalCalls) {
383         int state = CallState.NEW;
384         switch (call.getState()) {
385             case CallState.ABORTED:
386             case CallState.DISCONNECTED:
387                 state = android.telecom.Call.STATE_DISCONNECTED;
388                 break;
389             case CallState.ACTIVE:
390                 state = android.telecom.Call.STATE_ACTIVE;
391                 break;
392             case CallState.CONNECTING:
393                 state = android.telecom.Call.STATE_CONNECTING;
394                 break;
395             case CallState.DIALING:
396                 state = android.telecom.Call.STATE_DIALING;
397                 break;
398             case CallState.PULLING:
399                 if (supportsExternalCalls) {
400                     // The InCallService supports external calls, so it must handle
401                     // STATE_PULLING_CALL.
402                     state = android.telecom.Call.STATE_PULLING_CALL;
403                 } else {
404                     // The InCallService does NOT support external calls, so remap
405                     // STATE_PULLING_CALL to STATE_DIALING.  In essence, pulling a call can be seen
406                     // as a form of dialing, so it is appropriate for InCallServices which do not
407                     // handle external calls.
408                     state = android.telecom.Call.STATE_DIALING;
409                 }
410                 break;
411             case CallState.DISCONNECTING:
412                 state = android.telecom.Call.STATE_DISCONNECTING;
413                 break;
414             case CallState.NEW:
415                 state = android.telecom.Call.STATE_NEW;
416                 break;
417             case CallState.ON_HOLD:
418                 state = android.telecom.Call.STATE_HOLDING;
419                 break;
420             case CallState.RINGING:
421             case CallState.ANSWERED:
422                 // TODO: does in-call UI need to see ANSWERED?
423                 state = android.telecom.Call.STATE_RINGING;
424                 break;
425             case CallState.SELECT_PHONE_ACCOUNT:
426                 state = android.telecom.Call.STATE_SELECT_PHONE_ACCOUNT;
427                 break;
428             case CallState.AUDIO_PROCESSING:
429                 state = android.telecom.Call.STATE_AUDIO_PROCESSING;
430                 break;
431             case CallState.SIMULATED_RINGING:
432                 state = android.telecom.Call.STATE_SIMULATED_RINGING;
433                 break;
434         }
435 
436         // If we are marked as 'locally disconnecting' then mark ourselves as disconnecting instead.
437         // Unless we're disconnect*ED*, in which case leave it at that.
438         if (call.isLocallyDisconnecting() &&
439                 (state != android.telecom.Call.STATE_DISCONNECTED)) {
440             state = android.telecom.Call.STATE_DISCONNECTING;
441         }
442         return state;
443     }
444 
445     private static final int[] CONNECTION_TO_CALL_CAPABILITY = new int[] {
446         Connection.CAPABILITY_HOLD,
447         android.telecom.Call.Details.CAPABILITY_HOLD,
448 
449         Connection.CAPABILITY_SUPPORT_HOLD,
450         android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD,
451 
452         Connection.CAPABILITY_MERGE_CONFERENCE,
453         android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE,
454 
455         Connection.CAPABILITY_SWAP_CONFERENCE,
456         android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE,
457 
458         Connection.CAPABILITY_RESPOND_VIA_TEXT,
459         android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT,
460 
461         Connection.CAPABILITY_MUTE,
462         android.telecom.Call.Details.CAPABILITY_MUTE,
463 
464         Connection.CAPABILITY_MANAGE_CONFERENCE,
465         android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE,
466 
467         Connection.CAPABILITY_SUPPORTS_VT_LOCAL_RX,
468         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_RX,
469 
470         Connection.CAPABILITY_SUPPORTS_VT_LOCAL_TX,
471         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX,
472 
473         Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
474         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
475 
476         Connection.CAPABILITY_SUPPORTS_VT_REMOTE_RX,
477         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX,
478 
479         Connection.CAPABILITY_SUPPORTS_VT_REMOTE_TX,
480         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_TX,
481 
482         Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
483         android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
484 
485         Connection.CAPABILITY_SEPARATE_FROM_CONFERENCE,
486         android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE,
487 
488         Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
489         android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
490 
491         Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
492         android.telecom.Call.Details.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
493 
494         Connection.CAPABILITY_CAN_PAUSE_VIDEO,
495         android.telecom.Call.Details.CAPABILITY_CAN_PAUSE_VIDEO,
496 
497         Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION,
498         android.telecom.Call.Details.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION,
499 
500         Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
501         android.telecom.Call.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
502 
503         Connection.CAPABILITY_CAN_PULL_CALL,
504         android.telecom.Call.Details.CAPABILITY_CAN_PULL_CALL,
505 
506         Connection.CAPABILITY_SUPPORT_DEFLECT,
507         android.telecom.Call.Details.CAPABILITY_SUPPORT_DEFLECT,
508 
509         Connection.CAPABILITY_ADD_PARTICIPANT,
510         android.telecom.Call.Details.CAPABILITY_ADD_PARTICIPANT,
511 
512         Connection.CAPABILITY_TRANSFER,
513         android.telecom.Call.Details.CAPABILITY_TRANSFER,
514 
515         Connection.CAPABILITY_TRANSFER_CONSULTATIVE,
516         android.telecom.Call.Details.CAPABILITY_TRANSFER_CONSULTATIVE
517     };
518 
convertConnectionToCallCapabilities(int connectionCapabilities)519     private static int convertConnectionToCallCapabilities(int connectionCapabilities) {
520         int callCapabilities = 0;
521         for (int i = 0; i < CONNECTION_TO_CALL_CAPABILITY.length; i += 2) {
522             if ((CONNECTION_TO_CALL_CAPABILITY[i] & connectionCapabilities) ==
523                     CONNECTION_TO_CALL_CAPABILITY[i]) {
524 
525                 callCapabilities |= CONNECTION_TO_CALL_CAPABILITY[i + 1];
526             }
527         }
528         return callCapabilities;
529     }
530 
531     private static final int[] CONNECTION_TO_CALL_PROPERTIES = new int[] {
532         Connection.PROPERTY_HIGH_DEF_AUDIO,
533         android.telecom.Call.Details.PROPERTY_HIGH_DEF_AUDIO,
534 
535         Connection.PROPERTY_WIFI,
536         android.telecom.Call.Details.PROPERTY_WIFI,
537 
538         Connection.PROPERTY_GENERIC_CONFERENCE,
539         android.telecom.Call.Details.PROPERTY_GENERIC_CONFERENCE,
540 
541         Connection.PROPERTY_EMERGENCY_CALLBACK_MODE,
542         android.telecom.Call.Details.PROPERTY_EMERGENCY_CALLBACK_MODE,
543 
544         Connection.PROPERTY_IS_EXTERNAL_CALL,
545         android.telecom.Call.Details.PROPERTY_IS_EXTERNAL_CALL,
546 
547         Connection.PROPERTY_HAS_CDMA_VOICE_PRIVACY,
548         android.telecom.Call.Details.PROPERTY_HAS_CDMA_VOICE_PRIVACY,
549 
550         Connection.PROPERTY_SELF_MANAGED,
551         android.telecom.Call.Details.PROPERTY_SELF_MANAGED,
552 
553         Connection.PROPERTY_ASSISTED_DIALING,
554         android.telecom.Call.Details.PROPERTY_ASSISTED_DIALING,
555 
556         Connection.PROPERTY_IS_RTT,
557         android.telecom.Call.Details.PROPERTY_RTT,
558 
559         Connection.PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
560         android.telecom.Call.Details.PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
561 
562         Connection.PROPERTY_IS_ADHOC_CONFERENCE,
563         android.telecom.Call.Details.PROPERTY_IS_ADHOC_CONFERENCE
564     };
565 
convertConnectionToCallProperties(int connectionProperties)566     private static int convertConnectionToCallProperties(int connectionProperties) {
567         int callProperties = 0;
568         for (int i = 0; i < CONNECTION_TO_CALL_PROPERTIES.length; i += 2) {
569             if ((CONNECTION_TO_CALL_PROPERTIES[i] & connectionProperties) ==
570                     CONNECTION_TO_CALL_PROPERTIES[i]) {
571 
572                 callProperties |= CONNECTION_TO_CALL_PROPERTIES[i + 1];
573             }
574         }
575         return callProperties;
576     }
577 
578     /**
579      * Removes the specified capability from the set of capabilities bits and returns the new set.
580      */
removeCapability(int capabilities, int capability)581     private static int removeCapability(int capabilities, int capability) {
582         return capabilities & ~capability;
583     }
584 
getParcelableRttCall(Call call)585     private static ParcelableRttCall getParcelableRttCall(Call call) {
586         if (!call.isRttCall()) {
587             return null;
588         }
589         return new ParcelableRttCall(call.getRttMode(), call.getInCallToCsRttPipeForInCall(),
590                 call.getCsToInCallRttPipeForInCall());
591     }
592 
ParcelableCallUtils()593     private ParcelableCallUtils() {}
594 }
595