• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.provider.CallLog.Calls.MISSED_REASON_NOT_MISSED;
20 import static android.telephony.TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE;
21 
22 import static com.android.server.telecom.CachedCallback.TYPE_QUEUE;
23 import static com.android.server.telecom.CachedCallback.TYPE_STATE;
24 import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation
25         .TransactionalVideoStateToString;
26 import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation
27         .VideoProfileStateToTransactionalVideoState;
28 
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.pm.PackageManager;
34 import android.graphics.Bitmap;
35 import android.graphics.drawable.Drawable;
36 import android.net.Uri;
37 import android.os.Bundle;
38 import android.os.Handler;
39 import android.os.Looper;
40 import android.os.OutcomeReceiver;
41 import android.os.ParcelFileDescriptor;
42 import android.os.RemoteException;
43 import android.os.SystemClock;
44 import android.os.UserHandle;
45 import android.provider.CallLog;
46 import android.provider.ContactsContract.Contacts;
47 import android.telecom.BluetoothCallQualityReport;
48 import android.telecom.CallAttributes;
49 import android.telecom.CallAudioState;
50 import android.telecom.CallDiagnosticService;
51 import android.telecom.CallDiagnostics;
52 import android.telecom.CallException;
53 import android.telecom.CallerInfo;
54 import android.telecom.Conference;
55 import android.telecom.Connection;
56 import android.telecom.ConnectionService;
57 import android.telecom.DisconnectCause;
58 import android.telecom.GatewayInfo;
59 import android.telecom.Log;
60 import android.telecom.Logging.EventManager;
61 import android.telecom.ParcelableConference;
62 import android.telecom.ParcelableConnection;
63 import android.telecom.PhoneAccount;
64 import android.telecom.PhoneAccountHandle;
65 import android.telecom.StatusHints;
66 import android.telecom.TelecomManager;
67 import android.telecom.VideoProfile;
68 import android.telephony.CallQuality;
69 import android.telephony.PhoneNumberUtils;
70 import android.telephony.TelephonyManager;
71 import android.telephony.emergency.EmergencyNumber;
72 import android.telephony.ims.ImsReasonInfo;
73 import android.text.TextUtils;
74 import android.widget.Toast;
75 
76 import com.android.internal.annotations.VisibleForTesting;
77 import com.android.internal.telecom.IVideoProvider;
78 import com.android.internal.util.Preconditions;
79 import com.android.server.telecom.flags.FeatureFlags;
80 import com.android.server.telecom.stats.CallFailureCause;
81 import com.android.server.telecom.stats.CallStateChangedAtomWriter;
82 import com.android.server.telecom.ui.ToastFactory;
83 import com.android.server.telecom.callsequencing.CallTransaction;
84 import com.android.server.telecom.callsequencing.TransactionManager;
85 import com.android.server.telecom.callsequencing.VerifyCallStateChangeTransaction;
86 import com.android.server.telecom.callsequencing.CallTransactionResult;
87 
88 import java.io.IOException;
89 import java.text.SimpleDateFormat;
90 import java.util.ArrayList;
91 import java.util.Collection;
92 import java.util.Collections;
93 import java.util.Date;
94 import java.util.HashMap;
95 import java.util.LinkedList;
96 import java.util.List;
97 import java.util.Locale;
98 import java.util.Map;
99 import java.util.Objects;
100 import java.util.Set;
101 import java.util.concurrent.CompletableFuture;
102 import java.util.concurrent.ConcurrentHashMap;
103 import java.util.concurrent.ExecutionException;
104 import java.util.concurrent.TimeUnit;
105 import java.util.stream.Collectors;
106 
107 /**
108  *  Encapsulates all aspects of a given phone call throughout its lifecycle, starting
109  *  from the time the call intent was received by Telecom (vs. the time the call was
110  *  connected etc).
111  */
112 public class Call implements CreateConnectionResponse, EventManager.Loggable,
113         ConnectionServiceFocusManager.CallFocus {
114     public final static String CALL_ID_UNKNOWN = "-1";
115     public final static long DATA_USAGE_NOT_SET = -1;
116 
117     public static final int CALL_DIRECTION_UNDEFINED = 0;
118     public static final int CALL_DIRECTION_OUTGOING = 1;
119     public static final int CALL_DIRECTION_INCOMING = 2;
120     public static final int CALL_DIRECTION_UNKNOWN = 3;
121 
122     /** Identifies extras changes which originated from a connection service. */
123     public static final int SOURCE_CONNECTION_SERVICE = 1;
124     /** Identifies extras changes which originated from an incall service. */
125     public static final int SOURCE_INCALL_SERVICE = 2;
126 
127     private static final int RTT_PIPE_READ_SIDE_INDEX = 0;
128     private static final int RTT_PIPE_WRITE_SIDE_INDEX = 1;
129 
130     private static final int INVALID_RTT_REQUEST_ID = -1;
131 
132     private static final char NO_DTMF_TONE = '\0';
133 
134     /**
135      * The following simultaneous call types will be set on each call on creation and may be updated
136      * according to priority level. CALL_DIRECTION_DUAL_DIFF_ACCOUNT holds the highest priority.
137      * So if for example, a call is created with CALL_DIRECTION_DUAL_SAME_ACCOUNT, it can be
138      * upgraded to CALL_DIRECTION_DUAL_DIFF_ACCOUNT if another call is added with a different phone
139      * account.
140      */
141     public static final int CALL_SIMULTANEOUS_UNKNOWN = 0;
142     // Only used if simultaneous calling is not available
143     public static final int CALL_SIMULTANEOUS_DISABLED_SAME_ACCOUNT = 1;
144     // Only used if simultaneous calling is not available
145     public static final int CALL_SIMULTANEOUS_DISABLED_DIFF_ACCOUNT = 2;
146     public static final int CALL_DIRECTION_DUAL_SAME_ACCOUNT = 3;
147     public static final int CALL_DIRECTION_DUAL_DIFF_ACCOUNT = 4;
148 
149     /**
150      * Listener for CallState changes which can be leveraged by a Transaction.
151      */
152     public interface CallStateListener {
onCallStateChanged(int newCallState)153         void onCallStateChanged(int newCallState);
154     }
155 
156     public List<CallStateListener> mCallStateListeners = new ArrayList<>();
157 
addCallStateListener(CallStateListener newListener)158     public void addCallStateListener(CallStateListener newListener) {
159         mCallStateListeners.add(newListener);
160     }
161 
removeCallStateListener(CallStateListener newListener)162     public boolean removeCallStateListener(CallStateListener newListener) {
163         return mCallStateListeners.remove(newListener);
164     }
165 
166     /**
167      * Listener for events on the call.
168      */
169     public interface Listener {
onSuccessfulOutgoingCall(Call call, int callState)170         default void onSuccessfulOutgoingCall(Call call, int callState) {};
onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)171         default void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {};
onSuccessfulIncomingCall(Call call)172         default void onSuccessfulIncomingCall(Call call) {};
onFailedIncomingCall(Call call)173         default void onFailedIncomingCall(Call call) {};
onSuccessfulUnknownCall(Call call, int callState)174         default void onSuccessfulUnknownCall(Call call, int callState) {};
onFailedUnknownCall(Call call)175         default void onFailedUnknownCall(Call call) {};
onRingbackRequested(Call call, boolean ringbackRequested)176         default void onRingbackRequested(Call call, boolean ringbackRequested) {};
onPostDialWait(Call call, String remaining)177         default void onPostDialWait(Call call, String remaining) {};
onPostDialChar(Call call, char nextChar)178         default void onPostDialChar(Call call, char nextChar) {};
onConnectionCapabilitiesChanged(Call call)179         default void onConnectionCapabilitiesChanged(Call call) {};
onConnectionPropertiesChanged(Call call, boolean didRttChange)180         default void onConnectionPropertiesChanged(Call call, boolean didRttChange) {};
onParentChanged(Call call)181         default void onParentChanged(Call call) {};
onChildrenChanged(Call call)182         default void onChildrenChanged(Call call) {};
onCannedSmsResponsesLoaded(Call call)183         default void onCannedSmsResponsesLoaded(Call call) {};
onVideoCallProviderChanged(Call call)184         default void onVideoCallProviderChanged(Call call) {};
onCallerInfoChanged(Call call)185         default void onCallerInfoChanged(Call call) {};
onIsVoipAudioModeChanged(Call call)186         default void onIsVoipAudioModeChanged(Call call) {};
onStatusHintsChanged(Call call)187         default void onStatusHintsChanged(Call call) {};
onExtrasChanged(Call c, int source, Bundle extras, String requestingPackageName)188         default void onExtrasChanged(Call c, int source, Bundle extras,
189                 String requestingPackageName) {};
onExtrasRemoved(Call c, int source, List<String> keys)190         default void onExtrasRemoved(Call c, int source, List<String> keys) {};
onHandleChanged(Call call)191         default void onHandleChanged(Call call) {};
onCallerDisplayNameChanged(Call call)192         default void onCallerDisplayNameChanged(Call call) {};
onCallDirectionChanged(Call call)193         default void onCallDirectionChanged(Call call) {};
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)194         default void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {};
onTargetPhoneAccountChanged(Call call)195         default void onTargetPhoneAccountChanged(Call call) {};
onConnectionManagerPhoneAccountChanged(Call call)196         default void onConnectionManagerPhoneAccountChanged(Call call) {};
onPhoneAccountChanged(Call call)197         default void onPhoneAccountChanged(Call call) {};
onConferenceableCallsChanged(Call call)198         default void onConferenceableCallsChanged(Call call) {};
onConferenceStateChanged(Call call, boolean isConference)199         default void onConferenceStateChanged(Call call, boolean isConference) {};
onCdmaConferenceSwap(Call call)200         default void onCdmaConferenceSwap(Call call) {};
onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout)201         default boolean onCanceledViaNewOutgoingCallBroadcast(Call call,
202                 long disconnectionTimeout) {
203             return false;
204         };
onHoldToneRequested(Call call)205         default void onHoldToneRequested(Call call) {};
onCallHoldFailed(Call call)206         default void onCallHoldFailed(Call call) {};
onCallSwitchFailed(Call call)207         default void onCallSwitchFailed(Call call) {};
onCallResumeFailed(Call call)208         default void onCallResumeFailed(Call call) {};
onConnectionEvent(Call call, String event, Bundle extras)209         default void onConnectionEvent(Call call, String event, Bundle extras) {};
onCallStreamingStateChanged(Call call, boolean isStreaming)210         default void onCallStreamingStateChanged(Call call, boolean isStreaming) {}
onExternalCallChanged(Call call, boolean isExternalCall)211         default void onExternalCallChanged(Call call, boolean isExternalCall) {};
onRttInitiationFailure(Call call, int reason)212         default void onRttInitiationFailure(Call call, int reason) {};
onRemoteRttRequest(Call call, int requestId)213         default void onRemoteRttRequest(Call call, int requestId) {};
onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy)214         default void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
215                 Bundle extras, boolean isLegacy)  {};
onHandoverFailed(Call call, int error)216         default void onHandoverFailed(Call call, int error) {};
onHandoverComplete(Call call)217         default void onHandoverComplete(Call call)  {};
onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report)218         default void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report) {};
onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue)219         default void onReceivedDeviceToDeviceMessage(Call call, int messageType,
220                 int messageValue) {};
onReceivedCallQualityReport(Call call, CallQuality callQuality)221         default void onReceivedCallQualityReport(Call call, CallQuality callQuality) {};
onCallerNumberVerificationStatusChanged(Call call, int callerNumberVerificationStatus)222         default void onCallerNumberVerificationStatusChanged(Call call,
223                 int callerNumberVerificationStatus) {};
224     }
225 
226     public abstract static class ListenerBase implements Listener {
227         @Override
onSuccessfulOutgoingCall(Call call, int callState)228         public void onSuccessfulOutgoingCall(Call call, int callState) {}
229         @Override
onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)230         public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {}
231         @Override
onSuccessfulIncomingCall(Call call)232         public void onSuccessfulIncomingCall(Call call) {}
233         @Override
onFailedIncomingCall(Call call)234         public void onFailedIncomingCall(Call call) {}
235         @Override
onSuccessfulUnknownCall(Call call, int callState)236         public void onSuccessfulUnknownCall(Call call, int callState) {}
237         @Override
onFailedUnknownCall(Call call)238         public void onFailedUnknownCall(Call call) {}
239         @Override
onRingbackRequested(Call call, boolean ringbackRequested)240         public void onRingbackRequested(Call call, boolean ringbackRequested) {}
241         @Override
onPostDialWait(Call call, String remaining)242         public void onPostDialWait(Call call, String remaining) {}
243         @Override
onPostDialChar(Call call, char nextChar)244         public void onPostDialChar(Call call, char nextChar) {}
245         @Override
onConnectionCapabilitiesChanged(Call call)246         public void onConnectionCapabilitiesChanged(Call call) {}
247         @Override
onConnectionPropertiesChanged(Call call, boolean didRttChange)248         public void onConnectionPropertiesChanged(Call call, boolean didRttChange) {}
249         @Override
onParentChanged(Call call)250         public void onParentChanged(Call call) {}
251         @Override
onChildrenChanged(Call call)252         public void onChildrenChanged(Call call) {}
253         @Override
onCannedSmsResponsesLoaded(Call call)254         public void onCannedSmsResponsesLoaded(Call call) {}
255         @Override
onVideoCallProviderChanged(Call call)256         public void onVideoCallProviderChanged(Call call) {}
257         @Override
onCallerInfoChanged(Call call)258         public void onCallerInfoChanged(Call call) {}
259         @Override
onIsVoipAudioModeChanged(Call call)260         public void onIsVoipAudioModeChanged(Call call) {}
261         @Override
onStatusHintsChanged(Call call)262         public void onStatusHintsChanged(Call call) {}
263         @Override
onExtrasChanged(Call c, int source, Bundle extras, String requestingPackageName)264         public void onExtrasChanged(Call c, int source, Bundle extras,
265                 String requestingPackageName) {}
266         @Override
onExtrasRemoved(Call c, int source, List<String> keys)267         public void onExtrasRemoved(Call c, int source, List<String> keys) {}
268         @Override
onHandleChanged(Call call)269         public void onHandleChanged(Call call) {}
270         @Override
onCallerDisplayNameChanged(Call call)271         public void onCallerDisplayNameChanged(Call call) {}
272         @Override
onCallDirectionChanged(Call call)273         public void onCallDirectionChanged(Call call) {}
274         @Override
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)275         public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {}
276         @Override
onTargetPhoneAccountChanged(Call call)277         public void onTargetPhoneAccountChanged(Call call) {}
278         @Override
onConnectionManagerPhoneAccountChanged(Call call)279         public void onConnectionManagerPhoneAccountChanged(Call call) {}
280         @Override
onPhoneAccountChanged(Call call)281         public void onPhoneAccountChanged(Call call) {}
282         @Override
onConferenceableCallsChanged(Call call)283         public void onConferenceableCallsChanged(Call call) {}
284         @Override
onConferenceStateChanged(Call call, boolean isConference)285         public void onConferenceStateChanged(Call call, boolean isConference) {}
286         @Override
onCdmaConferenceSwap(Call call)287         public void onCdmaConferenceSwap(Call call) {}
288         @Override
onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout)289         public boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout) {
290             return false;
291         }
292         @Override
onHoldToneRequested(Call call)293         public void onHoldToneRequested(Call call) {}
294         @Override
onCallHoldFailed(Call call)295         public void onCallHoldFailed(Call call) {}
296         @Override
onCallSwitchFailed(Call call)297         public void onCallSwitchFailed(Call call) {}
298         @Override
onCallResumeFailed(Call call)299         public void onCallResumeFailed(Call call) {}
300         @Override
onConnectionEvent(Call call, String event, Bundle extras)301         public void onConnectionEvent(Call call, String event, Bundle extras) {}
302         @Override
onCallStreamingStateChanged(Call call, boolean isStreaming)303         public void onCallStreamingStateChanged(Call call, boolean isStreaming) {}
304         @Override
onExternalCallChanged(Call call, boolean isExternalCall)305         public void onExternalCallChanged(Call call, boolean isExternalCall) {}
306         @Override
onRttInitiationFailure(Call call, int reason)307         public void onRttInitiationFailure(Call call, int reason) {}
308         @Override
onRemoteRttRequest(Call call, int requestId)309         public void onRemoteRttRequest(Call call, int requestId) {}
310         @Override
onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy)311         public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
312                                         Bundle extras, boolean isLegacy) {}
313         @Override
onHandoverFailed(Call call, int error)314         public void onHandoverFailed(Call call, int error) {}
315         @Override
onHandoverComplete(Call call)316         public void onHandoverComplete(Call call) {}
317         @Override
onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report)318         public void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report) {}
319         @Override
onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue)320         public void onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue) {}
321         @Override
onReceivedCallQualityReport(Call call, CallQuality callQuality)322         public void onReceivedCallQualityReport(Call call, CallQuality callQuality) {}
323         @Override
onCallerNumberVerificationStatusChanged(Call call, int callerNumberVerificationStatus)324         public void onCallerNumberVerificationStatusChanged(Call call,
325                 int callerNumberVerificationStatus) {}
326     }
327 
328     private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener =
329             new CallerInfoLookupHelper.OnQueryCompleteListener() {
330                 /** ${inheritDoc} */
331                 @Override
332                 public void onCallerInfoQueryComplete(Uri handle, CallerInfo callerInfo) {
333                     synchronized (mLock) {
334                         Call call = Call.this;
335                         if (call != null) {
336                             call.setCallerInfo(handle, callerInfo);
337                         }
338                     }
339                 }
340 
341                 @Override
342                 public void onContactPhotoQueryComplete(Uri handle, CallerInfo callerInfo) {
343                     synchronized (mLock) {
344                         Call call = Call.this;
345                         if (call != null) {
346                             call.setCallerInfo(handle, callerInfo);
347                         }
348                     }
349                 }
350             };
351 
352     private final boolean mIsModifyStatePermissionGranted;
353     /**
354      * One of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING, or CALL_DIRECTION_UNKNOWN
355      */
356     private int mCallDirection;
357 
358     /**
359      * The post-dial digits that were dialed after the network portion of the number
360      */
361     private String mPostDialDigits;
362 
363     /**
364      * The secondary line number that an incoming call has been received on if the SIM subscription
365      * has multiple associated numbers.
366      */
367     private String mViaNumber = "";
368 
369     /**
370      * The wall clock time this call was created. Beyond logging and such, may also be used for
371      * bookkeeping and specifically for marking certain call attempts as failed attempts.
372      * Note: This timestamp should NOT be used for calculating call duration.
373      */
374     private long mCreationTimeMillis;
375 
376     /**
377      * The elapsed realtime millis when this call was created; this can be used to determine how
378      * long has elapsed since the call was first created.
379      */
380     private long mCreationElapsedRealtimeMillis;
381 
382     /** The time this call was made active. */
383     private long mConnectTimeMillis = 0;
384 
385     /**
386      * The time, in millis, since boot when this call was connected.  This should ONLY be used when
387      * calculating the duration of the call.
388      *
389      * The reason for this is that the {@link SystemClock#elapsedRealtime()} is based on the
390      * elapsed time since the device was booted.  Changes to the system clock (e.g. due to NITZ
391      * time sync, time zone changes user initiated clock changes) would cause a duration calculated
392      * based on {@link #mConnectTimeMillis} to change based on the delta in the time.
393      * Using the {@link SystemClock#elapsedRealtime()} ensures that changes to the wall clock do
394      * not impact the call duration.
395      */
396     private long mConnectElapsedTimeMillis = 0;
397 
398     /** The wall clock time this call was disconnected. */
399     private long mDisconnectTimeMillis = 0;
400 
401     /**
402      * The elapsed time since boot when this call was disconnected.  Recorded as the
403      * {@link SystemClock#elapsedRealtime()}.  This ensures that the call duration is not impacted
404      * by changes in the wall time clock.
405      */
406     private long mDisconnectElapsedTimeMillis = 0;
407 
408     /** The gateway information associated with this call. This stores the original call handle
409      * that the user is attempting to connect to via the gateway, the actual handle to dial in
410      * order to connect the call via the gateway, as well as the package name of the gateway
411      * service. */
412     private GatewayInfo mGatewayInfo;
413 
414     private PhoneAccountHandle mConnectionManagerPhoneAccountHandle;
415 
416     private PhoneAccountHandle mTargetPhoneAccountHandle;
417 
418     private PhoneAccountHandle mRemotePhoneAccountHandle;
419 
420     private UserHandle mAssociatedUser;
421 
422     private final Handler mHandler = new Handler(Looper.getMainLooper());
423 
424     private final List<Call> mConferenceableCalls = new ArrayList<>();
425 
426     /** The state of the call. */
427     private int mState;
428 
429     /**
430      * Determines whether the {@link ConnectionService} has responded to the initial request to
431      * create the connection.
432      *
433      * {@code false} indicates the {@link Call} has been added to Telecom, but the
434      * {@link Connection} has not yet been returned by the associated {@link ConnectionService}.
435      * {@code true} indicates the {@link Call} has an associated {@link Connection} reported by the
436      * {@link ConnectionService}.
437      */
438     private boolean mIsCreateConnectionComplete = false;
439 
440     /** The handle with which to establish this call. */
441     private Uri mHandle;
442 
443     /** The participants with which to establish adhoc conference call */
444     private List<Uri> mParticipants;
445     /**
446      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
447      */
448     private int mHandlePresentation;
449 
450     /**
451      * The verification status for an incoming call's number.
452      */
453     private @Connection.VerificationStatus int mCallerNumberVerificationStatus;
454 
455     /** The caller display name (CNAP) set by the connection service. */
456     private String mCallerDisplayName;
457 
458     /**
459      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
460      */
461     private int mCallerDisplayNamePresentation;
462 
463     /**
464      * The remote connection service which is attempted or already connecting this call. This is set
465      * to a non-null value only when a connection manager phone account is in use. When set, this
466      * will correspond to the target phone account of the {@link Call}.
467      */
468     private ConnectionServiceWrapper mRemoteConnectionService;
469 
470     /**
471      * The connection service which is attempted or already connecting this call.
472      */
473     private ConnectionServiceWrapper mConnectionService;
474 
475     private TransactionalServiceWrapper mTransactionalService;
476 
477     private boolean mIsEmergencyCall;
478 
479     /**
480      * Flag indicating if ECBM is active for the target phone account. This only applies to MT calls
481      * in the scenario of work profiles (when the profile is paused and the user has only registered
482      * a work sim). Normally, MT calls made to the work sim should be rejected when the work apps
483      * are paused. However, when the admin makes a MO ecall, ECBM should be enabled for that sim to
484      * allow non-emergency MT calls. MO calls don't apply because the phone account would be
485      * rejected from selection if the owner is not placing the call.
486      */
487     private boolean mIsInECBM;
488 
489     // The Call is considered an emergency call for testing, but will not actually connect to
490     // emergency services.
491     private boolean mIsTestEmergencyCall;
492 
493     private boolean mSpeakerphoneOn;
494 
495     private boolean mIsDisconnectingChildCall = false;
496 
497     /**
498      * Tracks the video states which were applicable over the duration of a call.
499      * See {@link VideoProfile} for a list of valid video states.
500      * <p>
501      * Video state history is tracked when the call is active, and when a call is rejected or
502      * missed.
503      */
504     private int mVideoStateHistory;
505 
506     private int mVideoState;
507 
508     /**
509      * Disconnect cause for the call. Only valid if the state of the call is STATE_DISCONNECTED.
510      * See {@link android.telecom.DisconnectCause}.
511      */
512     private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);
513 
514     /**
515      * Override the disconnect cause set by the connection service. Used for audio processing and
516      * simulated ringing calls as well as the condition when an emergency call is ended due to
517      * an emergency call being placed.
518      */
519     private DisconnectCause mOverrideDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);
520 
521     /**
522      * Simultaneous type of the call.
523      */
524     private int mSimultaneousType = CALL_SIMULTANEOUS_UNKNOWN;
525 
526     /**
527      * Indicate whether the call has the video
528      */
529     boolean mHasVideoCall;
530 
531     private Bundle mIntentExtras = new Bundle();
532 
533     /**
534      * The {@link Intent} which originally created this call.  Only populated when we are putting a
535      * call into a pending state and need to pick up initiation of the call later.
536      */
537     private Intent mOriginalCallIntent = null;
538 
539     /** Set of listeners on this call.
540      *
541      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
542      * load factor before resizing, 1 means we only expect a single thread to
543      * access the map so make only a single shard
544      */
545     private final Set<Listener> mListeners = Collections.newSetFromMap(
546             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
547 
548     private CreateConnectionProcessor mCreateConnectionProcessor;
549 
550     /** Caller information retrieved from the latest contact query. */
551     private CallerInfo mCallerInfo;
552 
553     /** The latest token used with a contact info query. */
554     private int mQueryToken = 0;
555 
556     /** Whether this call is requesting that Telecom play the ringback tone on its behalf. */
557     private boolean mRingbackRequested = false;
558 
559     /** Whether this call is requesting to be silently ringing. */
560     private boolean mSilentRingingRequested = false;
561 
562     /** Whether direct-to-voicemail query is pending. */
563     private boolean mDirectToVoicemailQueryPending;
564 
565     private int mConnectionCapabilities;
566 
567     private int mConnectionProperties;
568 
569     private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL;
570 
571     private boolean mIsConference = false;
572 
573     private boolean mHadChildren = false;
574 
575     private final boolean mShouldAttachToExistingConnection;
576 
577     private Call mParentCall = null;
578 
579     private List<Call> mChildCalls = new LinkedList<>();
580 
581     /** Set of text message responses allowed for this call, if applicable. */
582     private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
583 
584     /** Whether an attempt has been made to load the text message responses. */
585     private boolean mCannedSmsResponsesLoadingStarted = false;
586 
587     private VideoProviderProxy mVideoProviderProxy;
588 
589     private boolean mIsVoipAudioMode;
590     private StatusHints mStatusHints;
591     private Bundle mExtras;
592     private final ConnectionServiceRepository mRepository;
593     private final Context mContext;
594     private final CallsManager mCallsManager;
595     private final ClockProxy mClockProxy;
596     private final ToastFactory mToastFactory;
597     private final TelecomSystem.SyncRoot mLock;
598     private final String mId;
599     private String mConnectionId;
600     private Analytics.CallInfo mAnalytics = new Analytics.CallInfo();
601     private CallStateChangedAtomWriter mCallStateChangedAtomWriter =
602             new CallStateChangedAtomWriter();
603     private char mPlayingDtmfTone;
604 
605     private boolean mWasConferencePreviouslyMerged = false;
606     private boolean mWasHighDefAudio = false;
607     private boolean mWasWifi = false;
608     private boolean mWasVolte = false;
609     private boolean mDestroyed = false;
610 
611     // For conferences which support merge/swap at their level, we retain a notion of an active
612     // call. This is used for BluetoothPhoneService.  In order to support hold/merge, it must have
613     // the notion of the current "active" call within the conference call. This maintains the
614     // "active" call and switches every time the user hits "swap".
615     private Call mConferenceLevelActiveCall = null;
616 
617     private boolean mIsLocallyDisconnecting = false;
618 
619     /**
620      * Tracks the current call data usage as reported by the video provider.
621      */
622     private long mCallDataUsage = DATA_USAGE_NOT_SET;
623 
624     private boolean mIsWorkCall;
625 
626     /**
627      * Tracks whether this {@link Call}'s {@link #getTargetPhoneAccount()} has
628      * {@link PhoneAccount#EXTRA_PLAY_CALL_RECORDING_TONE} set.
629      */
630     private boolean mUseCallRecordingTone;
631 
632     // Set to true once the NewOutgoingCallIntentBroadcast comes back and is processed.
633     private boolean mIsNewOutgoingCallIntentBroadcastDone = false;
634 
635     /**
636      * Indicates whether the call is remotely held.  A call is considered remotely held when
637      * {@link #onConnectionEvent(String)} receives the {@link Connection#EVENT_ON_HOLD_TONE_START}
638      * event.
639      */
640     private boolean mIsRemotelyHeld = false;
641 
642     /**
643      * Indicates whether the {@link PhoneAccount} associated with this call is self-managed.
644      * See {@link PhoneAccount#CAPABILITY_SELF_MANAGED} for more information.
645      */
646     private boolean mIsSelfManaged = false;
647 
648     private boolean mIsTransactionalCall = false;
649     private CallingPackageIdentity mCallingPackageIdentity = new CallingPackageIdentity();
650     private boolean mSkipAutoUnhold = false;
651 
652     /**
653      * CallingPackageIdentity is responsible for storing properties about the calling package that
654      * initiated the call. For example, if MyVoipApp requests to add a call with Telecom, we can
655      * store their UID and PID when we are still bound to that package.
656      */
657     public static class CallingPackageIdentity {
658         public int mCallingPackageUid = -1;
659         public int mCallingPackagePid = -1;
660 
CallingPackageIdentity()661         public CallingPackageIdentity() {
662         }
663 
CallingPackageIdentity(Bundle extras)664         CallingPackageIdentity(Bundle extras) {
665             mCallingPackageUid = extras.getInt(CallAttributes.CALLER_UID_KEY, -1);
666             mCallingPackagePid = extras.getInt(CallAttributes.CALLER_PID_KEY, -1);
667         }
668     }
669 
670     /**
671      * Indicates whether this call is streaming.
672      */
673     private boolean mIsStreaming = false;
674 
675     /**
676      * Indicates whether the {@link PhoneAccount} associated with an self-managed call want to
677      * expose the call to an {@link android.telecom.InCallService} which declares the metadata
678      * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS},
679      * For calls that {@link #mIsSelfManaged} is {@code false}, this value should be {@code false}
680      * as well.
681      */
682     private boolean mVisibleToInCallService = false;
683 
684     /**
685      * Indicates whether the {@link PhoneAccount} associated with this call supports video calling.
686      * {@code True} if the phone account supports video calling, {@code false} otherwise.
687      */
688     private boolean mIsVideoCallingSupportedByPhoneAccount = false;
689 
690     /**
691      * Indicates whether this individual calls video state can be changed as opposed to be gated
692      * by the {@link PhoneAccount}.
693      *
694      * {@code True} if the call is Transactional && has the CallAttributes.SUPPORTS_VIDEO_CALLING
695      * capability {@code false} otherwise.
696      */
697     private boolean mTransactionalCallSupportsVideoCalling = false;
698 
setTransactionalCallSupportsVideoCalling(CallAttributes callAttributes)699     public void setTransactionalCallSupportsVideoCalling(CallAttributes callAttributes) {
700         if (!mIsTransactionalCall) {
701             Log.i(this, "setTransactionalCallSupportsVideoCalling: call is not transactional");
702             return;
703         }
704         if (callAttributes == null) {
705             Log.i(this, "setTransactionalCallSupportsVideoCalling: callAttributes is null");
706             return;
707         }
708         if ((callAttributes.getCallCapabilities() & CallAttributes.SUPPORTS_VIDEO_CALLING)
709                 == CallAttributes.SUPPORTS_VIDEO_CALLING) {
710             mTransactionalCallSupportsVideoCalling = true;
711         } else {
712             mTransactionalCallSupportsVideoCalling = false;
713         }
714     }
715 
isTransactionalCallSupportsVideoCalling()716     public boolean isTransactionalCallSupportsVideoCalling() {
717         return mTransactionalCallSupportsVideoCalling;
718     }
719 
720     /**
721      * Indicates whether or not this call can be pulled if it is an external call. If true, respect
722      * the Connection Capability set by the ConnectionService. If false, override the capability
723      * set and always remove the ability to pull this external call.
724      *
725      * See {@link #setIsPullExternalCallSupported(boolean)}
726      */
727     private boolean mIsPullExternalCallSupported = true;
728 
729     private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
730 
731     /**
732      * For {@link Connection}s or {@link android.telecom.Conference}s added via a ConnectionManager
733      * using the {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
734      * Connection)} or {@link android.telecom.ConnectionService#addConference(Conference)},
735      * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
736      * originally created it.
737      *
738      * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID} for more information.
739      */
740     private String mOriginalConnectionId;
741 
742     /**
743      * Two pairs of {@link android.os.ParcelFileDescriptor}s that handle RTT text communication
744      * between the in-call app and the connection service. If both non-null, this call should be
745      * treated as an RTT call.
746      * Each array should be of size 2. First one is the read side and the second one is the write
747      * side.
748      */
749     private ParcelFileDescriptor[] mInCallToConnectionServiceStreams;
750     private ParcelFileDescriptor[] mConnectionServiceToInCallStreams;
751 
752     /**
753      * True if we're supposed to start this call with RTT, either due to the settings switch or due
754      * to an extra.
755      */
756     private boolean mDidRequestToStartWithRtt = false;
757     /**
758      * Integer constant from {@link android.telecom.Call.RttCall}. Describes the current RTT mode.
759      */
760     private int mRttMode;
761     /**
762      * True if the call was ever an RTT call.
763      */
764     private boolean mWasEverRtt = false;
765 
766     /**
767      * Integer indicating the remote RTT request ID that is pending a response from the user.
768      */
769     private int mPendingRttRequestId = INVALID_RTT_REQUEST_ID;
770 
771     /**
772      * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
773      * int, Bundle, boolean)}, contains the call which this call is being handed over to.
774      */
775     private Call mHandoverDestinationCall = null;
776 
777     /**
778      * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
779      * int, Bundle, boolean)}, contains the call which this call is being handed over from.
780      */
781     private Call mHandoverSourceCall = null;
782 
783     /**
784      * The user-visible app name of the app that requested for this call to be put into the
785      * AUDIO_PROCESSING state. Used to display a notification to the user.
786      */
787     private CharSequence mAudioProcessingRequestingApp = null;
788 
789     /**
790      * Indicates the current state of this call if it is in the process of a handover.
791      */
792     private int mHandoverState = HandoverState.HANDOVER_NONE;
793 
794     /**
795      * Indicates whether this call is using one of the
796      * {@link com.android.server.telecom.callfiltering.CallFilter} modules.
797      */
798     private boolean mIsUsingCallFiltering = false;
799 
800     /**
801      * Indicates whether or not this call has been active before. This is helpful in detecting
802      * situations where we have moved into {@link CallState#SIMULATED_RINGING} or
803      * {@link CallState#AUDIO_PROCESSING} again after being active. If a call has moved into one
804      * of these states again after being active and the user dials an emergency call, we want to
805      * log these calls normally instead of considering them MISSED. If the emergency call was
806      * dialed during initial screening however, we want to treat those calls as MISSED (because the
807      * user never got the chance to explicitly reject).
808      */
809     private boolean mHasGoneActiveBefore = false;
810 
811     /**
812      * Indicates the package name of the {@link android.telecom.CallScreeningService} which should
813      * be sent the {@link android.telecom.TelecomManager#ACTION_POST_CALL} intent upon disconnection
814      * of a call.
815      */
816     private String mPostCallPackageName;
817 
818     /**
819      * Call missed information code.
820      */
821     @CallLog.Calls.MissedReason private long mMissedReason;
822 
823     /**
824      * Time that this call start ringing or simulated ringing.
825      */
826     private long mStartRingTime;
827 
828     /**
829      * The package name of the call screening service that silence this call. If the call is not
830      * silenced, this field will be null.
831      */
832     private CharSequence mCallScreeningAppName;
833 
834     /**
835      * The component name of the call screening service that silence this call. If the call is not
836      * silenced, this field will be null.
837      */
838     private String mCallScreeningComponentName;
839 
840     /**
841      * When {@code true} indicates this call originated from a SIM-based {@link PhoneAccount}.
842      * A sim-based {@link PhoneAccount} is one with {@link PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION}
843      * set.
844      */
845     private boolean mIsSimCall;
846 
847     /**
848      * Set to {@code true} if we received a valid response ({@code null} or otherwise) from
849      * the {@link CallDiagnostics#onCallDisconnected(ImsReasonInfo)} or
850      * {@link CallDiagnostics#onCallDisconnected(int, int)} calls.  This is used to detect a timeout
851      * when awaiting a response from the call diagnostic service.
852      */
853     private boolean mReceivedCallDiagnosticPostCallResponse = false;
854 
855     /**
856      * {@link CompletableFuture} used to delay posting disconnection and removal to a call until
857      * after a {@link CallDiagnosticService} is able to handle the disconnection and provide a
858      * disconnect message via {@link CallDiagnostics#onCallDisconnected(ImsReasonInfo)} or
859      * {@link CallDiagnostics#onCallDisconnected(int, int)}.
860      */
861     private CompletableFuture<Boolean> mDiagnosticCompleteFuture;
862 
863     /**
864      * {@link CompletableFuture} used to perform disconnect operations after
865      * {@link #mDiagnosticCompleteFuture} has completed.
866      */
867     private CompletableFuture<Void> mDisconnectFuture;
868 
869     /**
870      * {@link CompletableFuture} used to perform call removal operations after the
871      * {@link #mDisconnectFuture} has completed.
872      * <p>
873      * Note: It is possible for this future to be cancelled in the case that an internal operation
874      * will be handling clean up. (See {@link #setState}.)
875      */
876     private CompletableFuture<Void> mRemovalFuture;
877 
878     /**
879      * {@link CompletableFuture} used to delay audio routing change for a ringing call until the
880      * corresponding bluetooth {@link android.telecom.InCallService} is successfully bound or timed
881      * out.
882      */
883     private CompletableFuture<Boolean> mBtIcsFuture;
884 
885     /**
886      * Map of CachedCallbacks that are pending to be executed when the *ServiceWrapper connects
887      */
888     private final Map<String, List<CachedCallback>> mCachedServiceCallbacks = new HashMap<>();
889 
cacheServiceCallback(CachedCallback callback)890     public void cacheServiceCallback(CachedCallback callback) {
891         synchronized (mCachedServiceCallbacks) {
892             if (mFlags.cacheCallEvents()) {
893                 // If there are multiple threads caching + calling processCachedCallbacks at the
894                 // same time, there is a race - double check here to ensure that we do not lose an
895                 // operation due to a a cache happening after processCachedCallbacks.
896                 // Either service will be non-null in this case, but both will not be non-null
897                 if (mConnectionService != null) {
898                     callback.executeCallback(mConnectionService, this);
899                     return;
900                 }
901                 if (mTransactionalService != null) {
902                     callback.executeCallback(mTransactionalService, this);
903                     return;
904                 }
905             }
906             List<CachedCallback> cbs = mCachedServiceCallbacks.computeIfAbsent(
907                     callback.getCallbackId(), k -> new ArrayList<>());
908             switch (callback.getCacheType()) {
909                 case TYPE_STATE: {
910                     cbs.clear();
911                     cbs.add(callback);
912                     break;
913                 }
914                 case TYPE_QUEUE: {
915                     cbs.add(callback);
916                 }
917             }
918         }
919     }
920 
921     @VisibleForTesting
getCachedServiceCallbacksCopy()922     public Map<String, List<CachedCallback>> getCachedServiceCallbacksCopy() {
923         synchronized (mCachedServiceCallbacks) {
924             // This should only be used during testing, but to be safe, since there is internally a
925             // List value, we need to do a deep copy to ensure someone with a ref to the Map doesn't
926             // mutate the underlying list while we are modifying it in cacheServiceCallback.
927             return mCachedServiceCallbacks.entrySet().stream().collect(
928                     Collectors.toUnmodifiableMap(Map.Entry::getKey, e-> List.copyOf(e.getValue())));
929         }
930     }
931 
932     private FeatureFlags mFlags;
933 
934     /**
935      * Persists the specified parameters and initializes the new instance.
936      * @param context The context.
937      * @param repository The connection service repository.
938      * @param handle The handle to dial.
939      * @param gatewayInfo Gateway information to use for the call.
940      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
941      *         This account must be one that was registered with the
942      *           {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
943      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
944      *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
945      * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
946      *         or CALL_DIRECTION_UNKNOWN.
947      * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
948      * @param clockProxy
949      */
Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference, ClockProxy clockProxy, ToastFactory toastFactory, FeatureFlags featureFlags)950     public Call(
951             String callId,
952             Context context,
953             CallsManager callsManager,
954             TelecomSystem.SyncRoot lock,
955             ConnectionServiceRepository repository,
956             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
957             Uri handle,
958             GatewayInfo gatewayInfo,
959             PhoneAccountHandle connectionManagerPhoneAccountHandle,
960             PhoneAccountHandle targetPhoneAccountHandle,
961             int callDirection,
962             boolean shouldAttachToExistingConnection,
963             boolean isConference,
964             ClockProxy clockProxy,
965             ToastFactory toastFactory,
966             FeatureFlags featureFlags) {
967         this(callId, context, callsManager, lock, repository, phoneNumberUtilsAdapter,
968                handle, null, gatewayInfo, connectionManagerPhoneAccountHandle,
969                targetPhoneAccountHandle, callDirection, shouldAttachToExistingConnection,
970                isConference, clockProxy, toastFactory, featureFlags);
971 
972     }
973 
Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, List<Uri> participants, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference, ClockProxy clockProxy, ToastFactory toastFactory, FeatureFlags featureFlags)974     public Call(
975             String callId,
976             Context context,
977             CallsManager callsManager,
978             TelecomSystem.SyncRoot lock,
979             ConnectionServiceRepository repository,
980             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
981             Uri handle,
982             List<Uri> participants,
983             GatewayInfo gatewayInfo,
984             PhoneAccountHandle connectionManagerPhoneAccountHandle,
985             PhoneAccountHandle targetPhoneAccountHandle,
986             int callDirection,
987             boolean shouldAttachToExistingConnection,
988             boolean isConference,
989             ClockProxy clockProxy,
990             ToastFactory toastFactory,
991             FeatureFlags featureFlags) {
992         mFlags = featureFlags;
993         mId = callId;
994         mConnectionId = callId;
995         mState = (isConference && callDirection != CALL_DIRECTION_INCOMING &&
996                 callDirection != CALL_DIRECTION_OUTGOING) ?
997                 CallState.ACTIVE : CallState.NEW;
998         mContext = context;
999         mCallsManager = callsManager;
1000         mLock = lock;
1001         mRepository = repository;
1002         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
1003         mParticipants = participants;
1004         mPostDialDigits = handle != null
1005                 ? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : "";
1006         mGatewayInfo = gatewayInfo;
1007         setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
1008         mCallDirection = callDirection;
1009         setTargetPhoneAccount(targetPhoneAccountHandle);
1010         setHandle(handle);
1011         mIsConference = isConference;
1012         mShouldAttachToExistingConnection = shouldAttachToExistingConnection
1013                 || callDirection == CALL_DIRECTION_INCOMING;
1014         maybeLoadCannedSmsResponses();
1015         mClockProxy = clockProxy;
1016         mToastFactory = toastFactory;
1017         mCreationTimeMillis = mClockProxy.currentTimeMillis();
1018         mCreationElapsedRealtimeMillis = mClockProxy.elapsedRealtime();
1019         mMissedReason = MISSED_REASON_NOT_MISSED;
1020         mStartRingTime = 0;
1021 
1022         mCallStateChangedAtomWriter.setExistingCallCount(callsManager.getCalls().size());
1023         mIsModifyStatePermissionGranted =
1024                 isModifyPhoneStatePermissionGranted(getDelegatePhoneAccountHandle());
1025     }
1026 
1027     /**
1028      * Persists the specified parameters and initializes the new instance.
1029      * @param context The context.
1030      * @param repository The connection service repository.
1031      * @param handle The handle to dial.
1032      * @param gatewayInfo Gateway information to use for the call.
1033      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
1034      * This account must be one that was registered with the
1035      * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
1036      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
1037      * one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
1038      * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
1039      * or CALL_DIRECTION_UNKNOWN
1040      * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
1041      * connection, regardless of whether it's incoming or outgoing.
1042      * @param connectTimeMillis The connection time of the call.
1043      * @param clockProxy
1044      * @param featureFlags The telecom feature flags.
1045      */
Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference, long connectTimeMillis, long connectElapsedTimeMillis, ClockProxy clockProxy, ToastFactory toastFactory, FeatureFlags featureFlags)1046     Call(
1047             String callId,
1048             Context context,
1049             CallsManager callsManager,
1050             TelecomSystem.SyncRoot lock,
1051             ConnectionServiceRepository repository,
1052             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
1053             Uri handle,
1054             GatewayInfo gatewayInfo,
1055             PhoneAccountHandle connectionManagerPhoneAccountHandle,
1056             PhoneAccountHandle targetPhoneAccountHandle,
1057             int callDirection,
1058             boolean shouldAttachToExistingConnection,
1059             boolean isConference,
1060             long connectTimeMillis,
1061             long connectElapsedTimeMillis,
1062             ClockProxy clockProxy,
1063             ToastFactory toastFactory,
1064             FeatureFlags featureFlags) {
1065         this(callId, context, callsManager, lock, repository,
1066                 phoneNumberUtilsAdapter, handle, gatewayInfo,
1067                 connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
1068                 shouldAttachToExistingConnection, isConference, clockProxy, toastFactory,
1069                 featureFlags);
1070 
1071         mConnectTimeMillis = connectTimeMillis;
1072         mConnectElapsedTimeMillis = connectElapsedTimeMillis;
1073         mAnalytics.setCallStartTime(connectTimeMillis);
1074     }
1075 
addListener(Listener listener)1076     public void addListener(Listener listener) {
1077         mListeners.add(listener);
1078     }
1079 
removeListener(Listener listener)1080     public void removeListener(Listener listener) {
1081         if (listener != null) {
1082             mListeners.remove(listener);
1083         }
1084     }
1085 
initAnalytics()1086     public void initAnalytics() {
1087         initAnalytics(null, null);
1088     }
1089 
initAnalytics(String callingPackage, String extraCreationLogs)1090     public void initAnalytics(String callingPackage, String extraCreationLogs) {
1091         int analyticsDirection;
1092         switch (mCallDirection) {
1093             case CALL_DIRECTION_OUTGOING:
1094                 analyticsDirection = Analytics.OUTGOING_DIRECTION;
1095                 break;
1096             case CALL_DIRECTION_INCOMING:
1097                 analyticsDirection = Analytics.INCOMING_DIRECTION;
1098                 break;
1099             case CALL_DIRECTION_UNKNOWN:
1100             case CALL_DIRECTION_UNDEFINED:
1101             default:
1102                 analyticsDirection = Analytics.UNKNOWN_DIRECTION;
1103         }
1104         mAnalytics = Analytics.initiateCallAnalytics(mId, analyticsDirection);
1105         mAnalytics.setCallIsEmergency(mIsEmergencyCall);
1106         Log.addEvent(this, LogUtils.Events.CREATED, callingPackage + ";" + extraCreationLogs);
1107     }
1108 
getAnalytics()1109     public Analytics.CallInfo getAnalytics() {
1110         return mAnalytics;
1111     }
1112 
destroy()1113     public void destroy() {
1114         if (mDestroyed) {
1115             return;
1116         }
1117         // We should not keep these bitmaps around because the Call objects may be held for logging
1118         // purposes.
1119         // TODO: Make a container object that only stores the information we care about for Logging.
1120         if (mCallerInfo != null) {
1121             mCallerInfo.cachedPhotoIcon = null;
1122             mCallerInfo.cachedPhoto = null;
1123         }
1124         closeRttStreams();
1125 
1126         Log.addEvent(this, LogUtils.Events.DESTROYED);
1127         mDestroyed = true;
1128     }
1129 
closeRttStreams()1130     private void closeRttStreams() {
1131         if (mConnectionServiceToInCallStreams != null) {
1132             for (ParcelFileDescriptor fd : mConnectionServiceToInCallStreams) {
1133                 if (fd != null) {
1134                     try {
1135                         fd.close();
1136                     } catch (IOException e) {
1137                         // ignore
1138                     }
1139                 }
1140             }
1141         }
1142         if (mInCallToConnectionServiceStreams != null) {
1143             for (ParcelFileDescriptor fd : mInCallToConnectionServiceStreams) {
1144                 if (fd != null) {
1145                     try {
1146                         fd.close();
1147                     } catch (IOException e) {
1148                         // ignore
1149                     }
1150                 }
1151             }
1152         }
1153     }
1154 
1155     /** {@inheritDoc} */
1156     @Override
toString()1157     public String toString() {
1158         return String.format(Locale.US, "[Call id=%s, state=%s, tpac=%s, cmgr=%s, handle=%s, "
1159                         + "vidst=%s, childs(%d), has_parent(%b), cap=%s, prop=%s], voip=%b",
1160                 mId,
1161                 CallState.toString(getParcelableCallState()),
1162                 getTargetPhoneAccount(),
1163                 getConnectionManagerPhoneAccount(),
1164                 Log.piiHandle(mHandle),
1165                 getVideoStateDescription(getVideoState()),
1166                 getChildCalls().size(),
1167                 getParentCall() != null,
1168                 Connection.capabilitiesToStringShort(getConnectionCapabilities()),
1169                 Connection.propertiesToStringShort(getConnectionProperties()),
1170                 mIsVoipAudioMode);
1171     }
1172 
1173     @Override
getDescription()1174     public String getDescription() {
1175         StringBuilder s = new StringBuilder();
1176         if (isSelfManaged()) {
1177             s.append("SelfMgd Call");
1178         } else if (isExternalCall()) {
1179             s.append("External Call");
1180         } else {
1181             s.append("Call");
1182         }
1183         s.append(getId());
1184         s.append(" [");
1185         s.append(SimpleDateFormat.getDateTimeInstance().format(new Date(getCreationTimeMillis())));
1186         s.append("]");
1187         s.append(isIncoming() ? "(MT - incoming)" : "(MO - outgoing)");
1188         s.append("(User=");
1189         s.append(getAssociatedUser());
1190         s.append(")");
1191         s.append("\n\t");
1192 
1193         PhoneAccountHandle targetPhoneAccountHandle = getTargetPhoneAccount();
1194         PhoneAccountHandle remotePhoneAccountHandle = getRemotePhoneAccountHandle();
1195         PhoneAccountHandle connectionMgrAccountHandle = getConnectionManagerPhoneAccount();
1196         PhoneAccountHandle delegatePhoneAccountHandle = getDelegatePhoneAccountHandle();
1197         boolean isTargetSameAsRemote = targetPhoneAccountHandle != null
1198                 && targetPhoneAccountHandle.equals(remotePhoneAccountHandle);
1199         if (Objects.equals(delegatePhoneAccountHandle, targetPhoneAccountHandle)) {
1200             s.append(">>>");
1201         }
1202         s.append("Target");
1203         s.append(" PhoneAccount: ");
1204         if (targetPhoneAccountHandle != null) {
1205             s.append(targetPhoneAccountHandle);
1206             s.append(" (");
1207             s.append(getTargetPhoneAccountLabel());
1208             s.append(")");
1209             if (isTargetSameAsRemote) {
1210                 s.append("(remote)");
1211             }
1212         } else {
1213             s.append("not set");
1214         }
1215         if (!isTargetSameAsRemote && remotePhoneAccountHandle != null) {
1216             // This is a RARE case and will likely not be seen in practice but it is possible.
1217             if (delegatePhoneAccountHandle.equals(remotePhoneAccountHandle)) {
1218                 s.append("\n\t>>>Remote PhoneAccount: ");
1219             } else {
1220                 s.append("\n\tRemote PhoneAccount: ");
1221             }
1222             s.append(remotePhoneAccountHandle);
1223         }
1224         if (connectionMgrAccountHandle != null) {
1225             if (delegatePhoneAccountHandle.equals(connectionMgrAccountHandle)) {
1226                 s.append("\n\t>>>Conn mgr: ");
1227             } else {
1228                 s.append("\n\tConn mgr: ");
1229             }
1230             s.append(connectionMgrAccountHandle);
1231         }
1232 
1233         s.append("\n\tTo address: ");
1234         s.append(Log.piiHandle(getHandle()));
1235         if (isIncoming()) {
1236             switch (mCallerNumberVerificationStatus) {
1237                 case Connection.VERIFICATION_STATUS_FAILED:
1238                     s.append(" Verstat: fail");
1239                     break;
1240                 case Connection.VERIFICATION_STATUS_NOT_VERIFIED:
1241                     s.append(" Verstat: not");
1242                     break;
1243                 case Connection.VERIFICATION_STATUS_PASSED:
1244                     s.append(" Verstat: pass");
1245                     break;
1246             }
1247         }
1248         s.append(" Presentation: ");
1249         switch (getHandlePresentation()) {
1250             case TelecomManager.PRESENTATION_ALLOWED:
1251                 s.append("Allowed");
1252                 break;
1253             case TelecomManager.PRESENTATION_PAYPHONE:
1254                 s.append("Payphone");
1255                 break;
1256             case TelecomManager.PRESENTATION_RESTRICTED:
1257                 s.append("Restricted");
1258                 break;
1259             case TelecomManager.PRESENTATION_UNKNOWN:
1260                 s.append("Unknown");
1261                 break;
1262             case TelecomManager.PRESENTATION_UNAVAILABLE:
1263                 s.append("Unavailable");
1264                 break;
1265             default:
1266                 s.append("<undefined>");
1267         }
1268         s.append("\n");
1269         return s.toString();
1270     }
1271 
1272     /**
1273      * Builds a debug-friendly description string for a video state.
1274      * <p>
1275      * A = audio active, T = video transmission active, R = video reception active, P = video
1276      * paused.
1277      *
1278      * @param videoState The video state.
1279      * @return A string indicating which bits are set in the video state.
1280      */
getVideoStateDescription(int videoState)1281     private String getVideoStateDescription(int videoState) {
1282         StringBuilder sb = new StringBuilder();
1283         sb.append("A");
1284 
1285         if (VideoProfile.isTransmissionEnabled(videoState)) {
1286             sb.append("T");
1287         }
1288 
1289         if (VideoProfile.isReceptionEnabled(videoState)) {
1290             sb.append("R");
1291         }
1292 
1293         if (VideoProfile.isPaused(videoState)) {
1294             sb.append("P");
1295         }
1296 
1297         return sb.toString();
1298     }
1299 
1300     @Override
getConnectionServiceWrapper()1301     public ConnectionServiceFocusManager.ConnectionServiceFocus getConnectionServiceWrapper() {
1302         return (!mIsTransactionalCall ? mConnectionService : mTransactionalService);
1303     }
1304 
getState()1305     public int getState() {
1306         return mState;
1307     }
1308 
1309     /**
1310      * Similar to {@link #getState()}, except will return {@link CallState#DISCONNECTING} if the
1311      * call is locally disconnecting.  This is the call state which is reported to the
1312      * {@link android.telecom.InCallService}s when a call is parcelled.
1313      * @return The parcelable call state.
1314      */
getParcelableCallState()1315     public int getParcelableCallState() {
1316         if (isLocallyDisconnecting() &&
1317                 (mState != android.telecom.Call.STATE_DISCONNECTED)) {
1318             return CallState.DISCONNECTING;
1319         }
1320         return mState;
1321     }
1322 
1323     /**
1324      * Determines if this {@link Call} can receive call focus via the
1325      * {@link ConnectionServiceFocusManager}.
1326      * Only top-level calls and non-external calls are eligible.
1327      * @return {@code true} if this call is focusable, {@code false} otherwise.
1328      */
1329     @Override
isFocusable()1330     public boolean isFocusable() {
1331         boolean isChild = getParentCall() != null;
1332         return !isChild && !isExternalCall();
1333     }
1334 
shouldContinueProcessingAfterDisconnect()1335     private boolean shouldContinueProcessingAfterDisconnect() {
1336         // Stop processing once the call is active.
1337         if (!CreateConnectionTimeout.isCallBeingPlaced(this)) {
1338             return false;
1339         }
1340 
1341         // Only Redial a Call in the case of it being an Emergency Call.
1342         if(!isEmergencyCall()) {
1343             return false;
1344         }
1345 
1346         // Make sure that there are additional connection services to process.
1347         if (mCreateConnectionProcessor == null
1348             || !mCreateConnectionProcessor.isProcessingComplete()
1349             || !mCreateConnectionProcessor.hasMorePhoneAccounts()) {
1350             return false;
1351         }
1352 
1353         if (mDisconnectCause == null) {
1354             return false;
1355         }
1356 
1357         // Continue processing if the current attempt failed or timed out.
1358         return mDisconnectCause.getCode() == DisconnectCause.ERROR ||
1359             mCreateConnectionProcessor.isCallTimedOut();
1360     }
1361 
1362     /**
1363      * Returns the unique ID for this call as it exists in Telecom.
1364      * @return The call ID.
1365      */
getId()1366     public String getId() {
1367         return mId;
1368     }
1369 
1370     /**
1371      * Returns the unique ID for this call (see {@link #getId}) along with an attempt indicator that
1372      * iterates based on attempts to establish a {@link Connection} using createConnectionProcessor.
1373      * @return The call ID with an appended attempt id.
1374      */
getConnectionId()1375     public String getConnectionId() {
1376         if(mCreateConnectionProcessor != null) {
1377             mConnectionId = mId + "_" +
1378                     String.valueOf(mCreateConnectionProcessor.getConnectionAttempt());
1379             return mConnectionId;
1380         } else {
1381             return mConnectionId;
1382         }
1383     }
1384 
1385     /**
1386      * Handles an incoming overridden disconnect message for this call.
1387      *
1388      * We only care if the disconnect is handled via a future.
1389      * @param message the overridden disconnect message.
1390      */
handleOverrideDisconnectMessage(@ullable CharSequence message)1391     public void handleOverrideDisconnectMessage(@Nullable CharSequence message) {
1392         Log.i(this, "handleOverrideDisconnectMessage; callid=%s, msg=%s", getId(), message);
1393 
1394         if (isDisconnectHandledViaFuture()) {
1395             mReceivedCallDiagnosticPostCallResponse = true;
1396             if (message != null) {
1397                 Log.addEvent(this, LogUtils.Events.OVERRIDE_DISCONNECT_MESSAGE, message);
1398                 // Replace the existing disconnect cause in this call
1399                 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.ERROR, message,
1400                         message, null));
1401             }
1402 
1403             mDiagnosticCompleteFuture.complete(true);
1404         } else {
1405             Log.w(this, "handleOverrideDisconnectMessage; callid=%s - got override when unbound",
1406                     getId());
1407         }
1408     }
1409 
1410     /**
1411      * Sets the call state. Although there exists the notion of appropriate state transitions
1412      * (see {@link CallState}), in practice those expectations break down when cellular systems
1413      * misbehave and they do this very often. The result is that we do not enforce state transitions
1414      * and instead keep the code resilient to unexpected state changes.
1415      * @return true indicates if setState succeeded in setting the state to newState,
1416      * else it is failed, and the call is still in its original state.
1417      */
setState(int newState, String tag)1418     public boolean setState(int newState, String tag) {
1419         if (mState != newState) {
1420             Log.v(this, "setState %s -> %s", CallState.toString(mState),
1421                     CallState.toString(newState));
1422 
1423             if (newState == CallState.DISCONNECTED && shouldContinueProcessingAfterDisconnect()) {
1424                 Log.w(this, "continuing processing disconnected call with another service");
1425                 if (mFlags.cancelRemovalOnEmergencyRedial() && isDisconnectHandledViaFuture()
1426                         && isRemovalPending()) {
1427                     Log.i(this, "cancelling removal future in favor of "
1428                             + "CreateConnectionProcessor handling removal");
1429                     mRemovalFuture.cancel(true);
1430                 }
1431                 mCreateConnectionProcessor.continueProcessingIfPossible(this, mDisconnectCause);
1432                 return false;
1433             } else if (newState == CallState.ANSWERED && mState == CallState.ACTIVE) {
1434                 Log.w(this, "setState %s -> %s; call already active.", CallState.toString(mState),
1435                         CallState.toString(newState));
1436                 return false;
1437             }
1438 
1439             updateVideoHistoryViaState(mState, newState);
1440 
1441             mState = newState;
1442             maybeLoadCannedSmsResponses();
1443 
1444             if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
1445                 if (mConnectTimeMillis == 0) {
1446                     // We check to see if mConnectTime is already set to prevent the
1447                     // call from resetting active time when it goes in and out of
1448                     // ACTIVE/ON_HOLD
1449                     mConnectTimeMillis = mClockProxy.currentTimeMillis();
1450                     mConnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
1451                     mAnalytics.setCallStartTime(mConnectTimeMillis);
1452                 }
1453 
1454                 // We're clearly not disconnected, so reset the disconnected time.
1455                 mDisconnectTimeMillis = 0;
1456                 mDisconnectElapsedTimeMillis = 0;
1457                 mHasGoneActiveBefore = true;
1458             } else if (mState == CallState.DISCONNECTED) {
1459                 mDisconnectTimeMillis = mClockProxy.currentTimeMillis();
1460                 mDisconnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
1461                 mAnalytics.setCallEndTime(mDisconnectTimeMillis);
1462                 setLocallyDisconnecting(false);
1463                 fixParentAfterDisconnect();
1464             }
1465 
1466             // Log the state transition event
1467             String event = null;
1468             Object data = null;
1469             switch (newState) {
1470                 case CallState.ACTIVE:
1471                     event = LogUtils.Events.SET_ACTIVE;
1472                     break;
1473                 case CallState.CONNECTING:
1474                     event = LogUtils.Events.SET_CONNECTING;
1475                     break;
1476                 case CallState.DIALING:
1477                     event = LogUtils.Events.SET_DIALING;
1478                     break;
1479                 case CallState.PULLING:
1480                     event = LogUtils.Events.SET_PULLING;
1481                     break;
1482                 case CallState.DISCONNECTED:
1483                     event = LogUtils.Events.SET_DISCONNECTED;
1484                     data = getDisconnectCause();
1485                     break;
1486                 case CallState.DISCONNECTING:
1487                     event = LogUtils.Events.SET_DISCONNECTING;
1488                     break;
1489                 case CallState.ON_HOLD:
1490                     event = LogUtils.Events.SET_HOLD;
1491                     break;
1492                 case CallState.SELECT_PHONE_ACCOUNT:
1493                     event = LogUtils.Events.SET_SELECT_PHONE_ACCOUNT;
1494                     break;
1495                 case CallState.RINGING:
1496                     event = LogUtils.Events.SET_RINGING;
1497                     break;
1498                 case CallState.ANSWERED:
1499                     event = LogUtils.Events.SET_ANSWERED;
1500                     break;
1501                 case CallState.AUDIO_PROCESSING:
1502                     event = LogUtils.Events.SET_AUDIO_PROCESSING;
1503                     break;
1504                 case CallState.SIMULATED_RINGING:
1505                     event = LogUtils.Events.SET_SIMULATED_RINGING;
1506                     break;
1507             }
1508             if (event != null) {
1509                 // The string data should be just the tag.
1510                 String stringData = tag;
1511                 if (data != null) {
1512                     // If data exists, add it to tag.  If no tag, just use data.toString().
1513                     stringData = stringData == null ? data.toString() : stringData + "> " + data;
1514                 }
1515                 Log.addEvent(this, event, stringData);
1516             }
1517 
1518             if (mFlags.transactionalCsVerifier()) {
1519                 for (CallStateListener listener : mCallStateListeners) {
1520                     listener.onCallStateChanged(newState);
1521                 }
1522             }
1523 
1524             mCallStateChangedAtomWriter
1525                     .setDisconnectCause(getDisconnectCause())
1526                     .setSelfManaged(isSelfManaged())
1527                     .setExternalCall(isExternalCall())
1528                     .setEmergencyCall(isEmergencyCall())
1529                     .setDurationSeconds(Long.valueOf(
1530                         (mDisconnectTimeMillis - mConnectTimeMillis) / 1000).intValue())
1531                     .write(newState);
1532         }
1533         return true;
1534     }
1535 
setRingbackRequested(boolean ringbackRequested)1536     void setRingbackRequested(boolean ringbackRequested) {
1537         mRingbackRequested = ringbackRequested;
1538         for (Listener l : mListeners) {
1539             l.onRingbackRequested(this, mRingbackRequested);
1540         }
1541     }
1542 
isRingbackRequested()1543     public boolean isRingbackRequested() {
1544         return mRingbackRequested;
1545     }
1546 
setSilentRingingRequested(boolean silentRingingRequested)1547     public void setSilentRingingRequested(boolean silentRingingRequested) {
1548         mSilentRingingRequested = silentRingingRequested;
1549         Bundle bundle = new Bundle();
1550         bundle.putBoolean(android.telecom.Call.EXTRA_SILENT_RINGING_REQUESTED,
1551                 silentRingingRequested);
1552         putConnectionServiceExtras(bundle);
1553     }
1554 
isSilentRingingRequested()1555     public boolean isSilentRingingRequested() {
1556         return mSilentRingingRequested;
1557     }
1558 
setCallIsSuppressedByDoNotDisturb(boolean isCallSuppressed)1559     public void setCallIsSuppressedByDoNotDisturb(boolean isCallSuppressed) {
1560         Bundle bundle = new Bundle();
1561         bundle.putBoolean(android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB,
1562                 isCallSuppressed);
1563         putConnectionServiceExtras(bundle);
1564     }
1565 
isCallSuppressedByDoNotDisturb()1566     public boolean isCallSuppressedByDoNotDisturb() {
1567         if (getExtras() == null) {
1568             return false;
1569         }
1570         return getExtras().getBoolean(android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB);
1571     }
1572 
wasDndCheckComputedForCall()1573     public boolean wasDndCheckComputedForCall() {
1574         if (getExtras() == null) {
1575             return false;
1576         }
1577         return getExtras().containsKey(android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB);
1578     }
1579 
1580     @VisibleForTesting
isConference()1581     public boolean isConference() {
1582         return mIsConference;
1583     }
1584 
1585     /**
1586      * @return {@code true} if this call had children at some point, {@code false} otherwise.
1587      */
hadChildren()1588     public boolean hadChildren() {
1589         return mHadChildren;
1590     }
1591 
getHandle()1592     public Uri getHandle() {
1593         return mHandle;
1594     }
1595 
getParticipants()1596     public List<Uri> getParticipants() {
1597         return mParticipants;
1598     }
1599 
isAdhocConferenceCall()1600     public boolean isAdhocConferenceCall() {
1601         return mIsConference &&
1602                 (mCallDirection == CALL_DIRECTION_OUTGOING ||
1603                 mCallDirection == CALL_DIRECTION_INCOMING);
1604     }
1605 
getPostDialDigits()1606     public String getPostDialDigits() {
1607         return mPostDialDigits;
1608     }
1609 
clearPostDialDigits()1610     public void clearPostDialDigits() {
1611         mPostDialDigits = null;
1612     }
1613 
getViaNumber()1614     public String getViaNumber() {
1615         return mViaNumber;
1616     }
1617 
setViaNumber(String viaNumber)1618     public void setViaNumber(String viaNumber) {
1619         // If at any point the via number is not empty throughout the call, save that via number.
1620         if (!TextUtils.isEmpty(viaNumber)) {
1621             mViaNumber = viaNumber;
1622         }
1623     }
1624 
getHandlePresentation()1625     public int getHandlePresentation() {
1626         return mHandlePresentation;
1627     }
1628 
setCallerNumberVerificationStatus( @onnection.VerificationStatus int callerNumberVerificationStatus)1629     public void setCallerNumberVerificationStatus(
1630             @Connection.VerificationStatus int callerNumberVerificationStatus) {
1631         mCallerNumberVerificationStatus = callerNumberVerificationStatus;
1632         mListeners.forEach(l -> l.onCallerNumberVerificationStatusChanged(this,
1633                 callerNumberVerificationStatus));
1634     }
1635 
getCallerNumberVerificationStatus()1636     public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
1637         return mCallerNumberVerificationStatus;
1638     }
1639 
setHandle(Uri handle)1640     void setHandle(Uri handle) {
1641         setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
1642     }
1643 
setHandle(Uri handle, int presentation)1644     public void setHandle(Uri handle, int presentation) {
1645         if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
1646             mHandlePresentation = presentation;
1647             if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED ||
1648                     mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) {
1649                 mHandle = null;
1650             } else {
1651                 mHandle = handle;
1652                 if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme())
1653                         && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) {
1654                     // If the number is actually empty, set it to null, unless this is a
1655                     // SCHEME_VOICEMAIL uri which always has an empty number.
1656                     mHandle = null;
1657                 }
1658             }
1659 
1660             // Let's not allow resetting of the emergency flag. Once a call becomes an emergency
1661             // call, it will remain so for the rest of it's lifetime.
1662             if (!mIsEmergencyCall) {
1663                 try {
1664                     mIsEmergencyCall = mHandle != null &&
1665                             getTelephonyManager().isEmergencyNumber(
1666                                     mHandle.getSchemeSpecificPart());
1667                 } catch (UnsupportedOperationException use) {
1668                     Log.i(this, "setHandle: no FEATURE_TELEPHONY; emergency state unknown.");
1669                     mIsEmergencyCall = false;
1670                 } catch (IllegalStateException ise) {
1671                     Log.e(this, ise, "setHandle: can't determine if number is emergency");
1672                     mIsEmergencyCall = false;
1673                 } catch (RuntimeException r) {
1674                     Log.e(this, r, "setHandle: can't determine if number is emergency");
1675                     mIsEmergencyCall = false;
1676                 }
1677                 mAnalytics.setCallIsEmergency(mIsEmergencyCall);
1678             }
1679             if (!mIsTestEmergencyCall) {
1680                 mIsTestEmergencyCall = mHandle != null &&
1681                         isTestEmergencyCall(mHandle.getSchemeSpecificPart());
1682             }
1683             if (mTargetPhoneAccountHandle == null || !mContext.getResources().getString(
1684                     R.string.skip_incoming_caller_info_account_package).equalsIgnoreCase(
1685                     mTargetPhoneAccountHandle.getComponentName().getPackageName())) {
1686                 startCallerInfoLookup();
1687             } else {
1688                 Log.i(this, "skip incoming caller info lookup");
1689             }
1690             for (Listener l : mListeners) {
1691                 l.onHandleChanged(this);
1692             }
1693         }
1694     }
1695 
isTestEmergencyCall(String number)1696     private boolean isTestEmergencyCall(String number) {
1697         try {
1698             Map<Integer, List<EmergencyNumber>> eMap =
1699                     getTelephonyManager().getEmergencyNumberList();
1700             return eMap.values().stream().flatMap(Collection::stream)
1701                     .anyMatch(eNumber ->
1702                             eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST) &&
1703                                     number.equals(eNumber.getNumber()));
1704         } catch (UnsupportedOperationException uoe) {
1705             // No Telephony feature, so unable to determine.
1706             return false;
1707         } catch (IllegalStateException ise) {
1708             return false;
1709         } catch (RuntimeException r) {
1710             return false;
1711         }
1712     }
1713 
getContactPhotoUri()1714     public Uri getContactPhotoUri() {
1715         return mCallerInfo != null ? mCallerInfo.getContactDisplayPhotoUri() : null;
1716     }
1717 
getCallerDisplayName()1718     public String getCallerDisplayName() {
1719         return mCallerDisplayName;
1720     }
1721 
getCallerDisplayNamePresentation()1722     public int getCallerDisplayNamePresentation() {
1723         return mCallerDisplayNamePresentation;
1724     }
1725 
setCallerDisplayName(String callerDisplayName, int presentation)1726     void setCallerDisplayName(String callerDisplayName, int presentation) {
1727         if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
1728                 presentation != mCallerDisplayNamePresentation) {
1729             mCallerDisplayName = callerDisplayName;
1730             mCallerDisplayNamePresentation = presentation;
1731             for (Listener l : mListeners) {
1732                 l.onCallerDisplayNameChanged(this);
1733             }
1734         }
1735     }
1736 
setContactPhotoUri(Uri contactPhotoUri)1737     void setContactPhotoUri(Uri contactPhotoUri) {
1738         if (mCallerInfo != null) {
1739             mCallerInfo.SetContactDisplayPhotoUri(contactPhotoUri);
1740         }
1741     }
1742 
getName()1743     public String getName() {
1744         return mCallerInfo == null ? null : mCallerInfo.getName();
1745     }
1746 
getPhoneNumber()1747     public String getPhoneNumber() {
1748         return mCallerInfo == null ? null : mCallerInfo.getPhoneNumber();
1749     }
1750 
getPhotoIcon()1751     public Bitmap getPhotoIcon() {
1752         return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
1753     }
1754 
getPhoto()1755     public Drawable getPhoto() {
1756         return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
1757     }
1758 
1759     /**
1760      * @param cause The reason for the disconnection, represented by
1761      * {@link android.telecom.DisconnectCause}.
1762      */
setDisconnectCause(DisconnectCause cause)1763     public void setDisconnectCause(DisconnectCause cause) {
1764         // TODO: Consider combining this method with a setDisconnected() method that is totally
1765         // separate from setState.
1766 
1767         if (mOverrideDisconnectCause.getCode() != DisconnectCause.UNKNOWN) {
1768             cause = new DisconnectCause(mOverrideDisconnectCause.getCode(),
1769                     TextUtils.isEmpty(mOverrideDisconnectCause.getLabel()) ?
1770                             cause.getLabel() : mOverrideDisconnectCause.getLabel(),
1771                     (mOverrideDisconnectCause.getDescription() == null) ?
1772                             cause.getDescription() :mOverrideDisconnectCause.getDescription(),
1773                     TextUtils.isEmpty(mOverrideDisconnectCause.getReason()) ?
1774                             cause.getReason() : mOverrideDisconnectCause.getReason(),
1775                     (mOverrideDisconnectCause.getTone() == 0) ?
1776                             cause.getTone() : mOverrideDisconnectCause.getTone());
1777         }
1778         mAnalytics.setCallDisconnectCause(cause);
1779         mDisconnectCause = cause;
1780     }
1781 
setOverrideDisconnectCauseCode(DisconnectCause overrideDisconnectCause)1782     public void setOverrideDisconnectCauseCode(DisconnectCause overrideDisconnectCause) {
1783         mOverrideDisconnectCause = overrideDisconnectCause;
1784     }
1785 
1786 
getDisconnectCause()1787     public DisconnectCause getDisconnectCause() {
1788         return mDisconnectCause;
1789     }
1790 
1791     /**
1792      * @return {@code true} if this is an outgoing call to emergency services. An outgoing call is
1793      * identified as an emergency call by the dialer phone number.
1794      */
isEmergencyCall()1795     public boolean isEmergencyCall() {
1796         return mIsEmergencyCall;
1797     }
1798 
1799     /**
1800      * For testing purposes, set if this call is an emergency call or not.
1801      * @param isEmergencyCall {@code true} if emergency, {@code false} otherwise.
1802      */
1803     @VisibleForTesting
setIsEmergencyCall(boolean isEmergencyCall)1804     public void setIsEmergencyCall(boolean isEmergencyCall) {
1805         mIsEmergencyCall = isEmergencyCall;
1806     }
1807 
1808     /**
1809      * @return {@code true} if this an outgoing call to a test emergency number (and NOT to
1810      * emergency services). Used for testing purposes to differentiate between a real and fake
1811      * emergency call for safety reasons during testing.
1812      */
isTestEmergencyCall()1813     public boolean isTestEmergencyCall() {
1814         return mIsTestEmergencyCall;
1815     }
1816 
1817     /**
1818      * @return {@code true} if the target phone account is in ECBM.
1819      */
isInECBM()1820     public boolean isInECBM() {
1821         return mIsInECBM;
1822     }
1823 
1824     /**
1825      * Set if the target phone account is in ECBM.
1826      * @param isInEcbm {@code true} if target phone account is in ECBM, {@code false} otherwise.
1827      */
setIsInECBM(boolean isInECBM)1828     public void setIsInECBM(boolean isInECBM) {
1829         mIsInECBM = isInECBM;
1830     }
1831 
1832     /**
1833      * @return {@code true} if the network has identified this call as an emergency call.
1834      */
isNetworkIdentifiedEmergencyCall()1835     public boolean isNetworkIdentifiedEmergencyCall() {
1836         return hasProperty(Connection.PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL);
1837     }
1838 
1839     /**
1840      * @return The original handle this call is associated with. In-call services should use this
1841      * handle when indicating in their UI the handle that is being called.
1842      */
getOriginalHandle()1843     public Uri getOriginalHandle() {
1844         if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
1845             return mGatewayInfo.getOriginalAddress();
1846         }
1847         return getHandle();
1848     }
1849 
1850     @VisibleForTesting
getGatewayInfo()1851     public GatewayInfo getGatewayInfo() {
1852         return mGatewayInfo;
1853     }
1854 
setGatewayInfo(GatewayInfo gatewayInfo)1855     void setGatewayInfo(GatewayInfo gatewayInfo) {
1856         mGatewayInfo = gatewayInfo;
1857     }
1858 
1859     @VisibleForTesting
getConnectionManagerPhoneAccount()1860     public PhoneAccountHandle getConnectionManagerPhoneAccount() {
1861         return mConnectionManagerPhoneAccountHandle;
1862     }
1863 
1864     @VisibleForTesting
setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle)1865     public void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
1866         if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
1867             mConnectionManagerPhoneAccountHandle = accountHandle;
1868             for (Listener l : mListeners) {
1869                 l.onConnectionManagerPhoneAccountChanged(this);
1870             }
1871         }
1872         checkIfRttCapable();
1873     }
1874 
1875     /**
1876      * @return the {@link PhoneAccountHandle} of the remote connection service which placing this
1877      * call was delegated to, or {@code null} if a remote connection service was not used.
1878      */
getRemotePhoneAccountHandle()1879     public @Nullable PhoneAccountHandle getRemotePhoneAccountHandle() {
1880         return mRemotePhoneAccountHandle;
1881     }
1882 
1883     /**
1884      * Sets the {@link PhoneAccountHandle} of the remote connection service which placing this
1885      * call was delegated to.
1886      * @param accountHandle The phone account handle.
1887      */
setRemotePhoneAccountHandle(PhoneAccountHandle accountHandle)1888     public void setRemotePhoneAccountHandle(PhoneAccountHandle accountHandle) {
1889         mRemotePhoneAccountHandle = accountHandle;
1890     }
1891 
1892     /**
1893      * Determines which {@link PhoneAccountHandle} is actually placing a call.
1894      * Where {@link #getRemotePhoneAccountHandle()} is non-null, the connection manager is placing
1895      * the call via a remote connection service, so the remote connection service's phone account
1896      * is the source.
1897      * Where {@link #getConnectionManagerPhoneAccount()} is non-null and
1898      * {@link #getRemotePhoneAccountHandle()} is null, the connection manager is placing the call
1899      * itself (even if the target specifies something else).
1900      * Finally, if neither of the above cases apply, the target phone account is the one actually
1901      * placing the call.
1902      * @return The {@link PhoneAccountHandle} which is actually placing a call.
1903      */
getDelegatePhoneAccountHandle()1904     public @NonNull PhoneAccountHandle getDelegatePhoneAccountHandle() {
1905         if (mRemotePhoneAccountHandle != null) {
1906             return mRemotePhoneAccountHandle;
1907         }
1908         if (mConnectionManagerPhoneAccountHandle != null) {
1909             return mConnectionManagerPhoneAccountHandle;
1910         }
1911         return mTargetPhoneAccountHandle;
1912     }
1913 
getTargetPhoneAccount()1914     public PhoneAccountHandle getTargetPhoneAccount() {
1915         return mTargetPhoneAccountHandle;
1916     }
1917 
1918     @VisibleForTesting
setTargetPhoneAccount(PhoneAccountHandle accountHandle)1919     public void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
1920         if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
1921             mTargetPhoneAccountHandle = accountHandle;
1922             // Update the last MO emergency call in the helper, if applicable.
1923             if (isEmergencyCall() && !isIncoming()) {
1924                 mCallsManager.getEmergencyCallHelper().setLastOutgoingEmergencyCallPAH(
1925                         accountHandle);
1926             }
1927             for (Listener l : mListeners) {
1928                 l.onTargetPhoneAccountChanged(this);
1929             }
1930             configureCallAttributes();
1931         }
1932         checkIfVideoCapable();
1933         checkIfRttCapable();
1934 
1935         if (accountHandle != null) {
1936             mCallStateChangedAtomWriter.setUid(
1937                     accountHandle.getComponentName().getPackageName(),
1938                     mContext.getPackageManager());
1939             // Set the associated user for the call for MT calls based on the target phone account.
1940             UserHandle associatedUser = UserUtil.getAssociatedUserForCall(
1941                     mFlags.associatedUserRefactorForWorkProfile(),
1942                     mCallsManager.getPhoneAccountRegistrar(), mCallsManager.getCurrentUserHandle(),
1943                     accountHandle);
1944             if (isIncoming() && !associatedUser.equals(mAssociatedUser)) {
1945                 setAssociatedUser(associatedUser);
1946             }
1947         }
1948     }
1949 
getPhoneAccountFromHandle()1950     public PhoneAccount getPhoneAccountFromHandle() {
1951         if (getTargetPhoneAccount() == null) {
1952             return null;
1953         }
1954         PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1955                 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1956 
1957         if (phoneAccount == null) {
1958             return null;
1959         }
1960 
1961         return phoneAccount;
1962     }
1963 
getTargetPhoneAccountLabel()1964     public CharSequence getTargetPhoneAccountLabel() {
1965         if (getTargetPhoneAccount() == null) {
1966             return null;
1967         }
1968         PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1969                 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1970 
1971         if (phoneAccount == null) {
1972             return null;
1973         }
1974 
1975         return phoneAccount.getLabel();
1976     }
1977 
1978     /**
1979      * Determines if this Call should be written to the call log.
1980      * @return {@code true} for managed calls or for self-managed calls which have the
1981      * {@link PhoneAccount#EXTRA_LOG_SELF_MANAGED_CALLS} extra set.
1982      */
isLoggedSelfManaged()1983     public boolean isLoggedSelfManaged() {
1984         if (!isSelfManaged()) {
1985             // Managed calls are always logged.
1986             return true;
1987         }
1988         if (getTargetPhoneAccount() == null) {
1989             return false;
1990         }
1991         PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1992                 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1993 
1994         if (phoneAccount == null) {
1995             return false;
1996         }
1997 
1998         if (getHandle() == null) {
1999             // No point in logging a null-handle call. Some self-managed calls will have this.
2000             return false;
2001         }
2002 
2003         if (!PhoneAccount.SCHEME_SIP.equals(getHandle().getScheme()) &&
2004                 !PhoneAccount.SCHEME_TEL.equals(getHandle().getScheme())) {
2005             // Can't log schemes other than SIP or TEL for now.
2006             return false;
2007         }
2008 
2009         return phoneAccount.getExtras() != null && phoneAccount.getExtras().getBoolean(
2010                 PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, false);
2011     }
2012 
isIncoming()2013     public boolean isIncoming() {
2014         return mCallDirection == CALL_DIRECTION_INCOMING;
2015     }
2016 
isExternalCall()2017     public boolean isExternalCall() {
2018         return (getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) ==
2019                 Connection.PROPERTY_IS_EXTERNAL_CALL;
2020     }
2021 
isWorkCall()2022     public boolean isWorkCall() {
2023         return mIsWorkCall;
2024     }
2025 
isUsingCallRecordingTone()2026     public boolean isUsingCallRecordingTone() {
2027         return mUseCallRecordingTone;
2028     }
2029 
2030     /**
2031      * @return {@code true} if the {@link Call}'s {@link #getTargetPhoneAccount()} supports video.
2032      */
isVideoCallingSupportedByPhoneAccount()2033     public boolean isVideoCallingSupportedByPhoneAccount() {
2034         return mIsVideoCallingSupportedByPhoneAccount;
2035     }
2036 
2037     /**
2038      * Sets whether video calling is supported by the current phone account. Since video support
2039      * can change during a call, this method facilitates updating call video state.
2040      * @param isVideoCallingSupported Sets whether video calling is supported.
2041      */
setVideoCallingSupportedByPhoneAccount(boolean isVideoCallingSupported)2042     public void setVideoCallingSupportedByPhoneAccount(boolean isVideoCallingSupported) {
2043         if (mIsVideoCallingSupportedByPhoneAccount == isVideoCallingSupported) {
2044             return;
2045         }
2046         Log.i(this, "setVideoCallingSupportedByPhoneAccount: isSupp=%b", isVideoCallingSupported);
2047         mIsVideoCallingSupportedByPhoneAccount = isVideoCallingSupported;
2048 
2049         // Force an update of the connection capabilities so that the dialer is informed of the new
2050         // video capabilities based on the phone account's support for video.
2051         setConnectionCapabilities(getConnectionCapabilities(), true /* force */);
2052     }
2053 
2054     /**
2055      * Determines if pulling this external call is supported. If it is supported, we will allow the
2056      * {@link Connection#CAPABILITY_CAN_PULL_CALL} capability to be added to this call's
2057      * capabilities. If it is not supported, we will strip this capability before sending this
2058      * call's capabilities to the InCallService.
2059      * @param isPullExternalCallSupported true, if pulling this external call is supported, false
2060      *                                    otherwise.
2061      */
setIsPullExternalCallSupported(boolean isPullExternalCallSupported)2062     public void setIsPullExternalCallSupported(boolean isPullExternalCallSupported) {
2063         if (!isExternalCall()) return;
2064         if (isPullExternalCallSupported == mIsPullExternalCallSupported) return;
2065 
2066         Log.i(this, "setCanPullExternalCall: canPull=%b", isPullExternalCallSupported);
2067 
2068         mIsPullExternalCallSupported = isPullExternalCallSupported;
2069 
2070         // Use mConnectionCapabilities here to get the unstripped capabilities.
2071         setConnectionCapabilities(mConnectionCapabilities, true /* force */);
2072     }
2073 
2074     /**
2075      * @return {@code true} if the {@link Call} locally supports video.
2076      */
isLocallyVideoCapable()2077     public boolean isLocallyVideoCapable() {
2078         return (getConnectionCapabilities() & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
2079                 == Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL;
2080     }
2081 
isSelfManaged()2082     public boolean isSelfManaged() {
2083         return mIsSelfManaged;
2084     }
2085 
setIsSelfManaged(boolean isSelfManaged)2086     public void setIsSelfManaged(boolean isSelfManaged) {
2087         mIsSelfManaged = isSelfManaged;
2088 
2089         // Connection properties will add/remove the PROPERTY_SELF_MANAGED.
2090         setConnectionProperties(getConnectionProperties());
2091     }
2092 
isTransactionalCall()2093     public boolean isTransactionalCall() {
2094         return mIsTransactionalCall;
2095     }
2096 
setIsTransactionalCall(boolean isTransactionalCall)2097     public void setIsTransactionalCall(boolean isTransactionalCall) {
2098         mIsTransactionalCall = isTransactionalCall;
2099 
2100         // Connection properties will add/remove the PROPERTY_SELF_MANAGED.
2101         setConnectionProperties(getConnectionProperties());
2102     }
2103 
setCallingPackageIdentity(Bundle extras)2104     public void setCallingPackageIdentity(Bundle extras) {
2105         mCallingPackageIdentity = new CallingPackageIdentity(extras);
2106         // These extras should NOT be propagated to Dialer and should be removed.
2107         extras.remove(CallAttributes.CALLER_PID_KEY);
2108         extras.remove(CallAttributes.CALLER_UID_KEY);
2109     }
2110 
getCallingPackageIdentity()2111     public CallingPackageIdentity getCallingPackageIdentity() {
2112         return mCallingPackageIdentity;
2113     }
2114 
setTransactionServiceWrapper(TransactionalServiceWrapper service)2115     public void setTransactionServiceWrapper(TransactionalServiceWrapper service) {
2116         Log.i(this, "setTransactionServiceWrapper: service=[%s]", service);
2117         mTransactionalService = service;
2118         processCachedCallbacks(service);
2119     }
2120 
processCachedCallbacks(CallSourceService service)2121     private void processCachedCallbacks(CallSourceService service) {
2122         if(mFlags.cacheCallAudioCallbacks()) {
2123             synchronized (mCachedServiceCallbacks) {
2124                 for (List<CachedCallback> callbacks : mCachedServiceCallbacks.values()) {
2125                     callbacks.forEach( callback -> callback.executeCallback(service, this));
2126                 }
2127                 // clear list for memory cleanup purposes. The Service should never be reset
2128                 mCachedServiceCallbacks.clear();
2129             }
2130         }
2131     }
2132 
getService()2133     public CallSourceService getService() {
2134         if (isTransactionalCall()) {
2135             return mTransactionalService;
2136         } else {
2137             return mConnectionService;
2138         }
2139     }
2140 
getTransactionServiceWrapper()2141     public TransactionalServiceWrapper getTransactionServiceWrapper() {
2142         return mTransactionalService;
2143     }
2144 
visibleToInCallService()2145     public boolean visibleToInCallService() {
2146         return mVisibleToInCallService;
2147     }
2148 
setVisibleToInCallService(boolean visibleToInCallService)2149     public void setVisibleToInCallService(boolean visibleToInCallService) {
2150         mVisibleToInCallService = visibleToInCallService;
2151     }
2152 
markFinishedHandoverStateAndCleanup(int handoverState)2153     public void markFinishedHandoverStateAndCleanup(int handoverState) {
2154         if (mHandoverSourceCall != null) {
2155             mHandoverSourceCall.setHandoverState(handoverState);
2156         } else if (mHandoverDestinationCall != null) {
2157             mHandoverDestinationCall.setHandoverState(handoverState);
2158         }
2159         setHandoverState(handoverState);
2160         maybeCleanupHandover();
2161     }
2162 
maybeCleanupHandover()2163     public void maybeCleanupHandover() {
2164         if (mHandoverSourceCall != null) {
2165             mHandoverSourceCall.setHandoverSourceCall(null);
2166             mHandoverSourceCall.setHandoverDestinationCall(null);
2167             mHandoverSourceCall = null;
2168         } else if (mHandoverDestinationCall != null) {
2169             mHandoverDestinationCall.setHandoverSourceCall(null);
2170             mHandoverDestinationCall.setHandoverDestinationCall(null);
2171             mHandoverDestinationCall = null;
2172         }
2173     }
2174 
isHandoverInProgress()2175     public boolean isHandoverInProgress() {
2176         return mHandoverSourceCall != null || mHandoverDestinationCall != null;
2177     }
2178 
getHandoverDestinationCall()2179     public Call getHandoverDestinationCall() {
2180         return mHandoverDestinationCall;
2181     }
2182 
setHandoverDestinationCall(Call call)2183     public void setHandoverDestinationCall(Call call) {
2184         mHandoverDestinationCall = call;
2185     }
2186 
getHandoverSourceCall()2187     public Call getHandoverSourceCall() {
2188         return mHandoverSourceCall;
2189     }
2190 
setHandoverSourceCall(Call call)2191     public void setHandoverSourceCall(Call call) {
2192         mHandoverSourceCall = call;
2193     }
2194 
setHandoverState(int handoverState)2195     public void setHandoverState(int handoverState) {
2196         Log.d(this, "setHandoverState: callId=%s, handoverState=%s", getId(),
2197                 HandoverState.stateToString(handoverState));
2198         mHandoverState = handoverState;
2199     }
2200 
getHandoverState()2201     public int getHandoverState() {
2202         return mHandoverState;
2203     }
2204 
configureCallAttributes()2205     private void configureCallAttributes() {
2206         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
2207         boolean isWorkCall = false;
2208         boolean isCallRecordingToneSupported = false;
2209         boolean isSimCall = false;
2210         PhoneAccount phoneAccount =
2211                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
2212         if (phoneAccount != null) {
2213             final UserHandle userHandle;
2214             if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
2215                 userHandle = mAssociatedUser;
2216             } else {
2217                 userHandle = mTargetPhoneAccountHandle.getUserHandle();
2218             }
2219             if (userHandle != null) {
2220                 isWorkCall = UserUtil.isManagedProfile(mContext, userHandle, mFlags);
2221             }
2222 
2223             if (!mFlags.telecomResolveHiddenDependencies()) {
2224                 isCallRecordingToneSupported = (phoneAccount.hasCapabilities(
2225                         PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
2226                         && phoneAccount.getExtras() != null
2227                         && phoneAccount.getExtras().getBoolean(
2228                         PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, false));
2229             } else {
2230                 isCallRecordingToneSupported = false;
2231             }
2232             isSimCall = phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
2233         }
2234         mIsWorkCall = isWorkCall;
2235         mUseCallRecordingTone = isCallRecordingToneSupported;
2236         mIsSimCall = isSimCall;
2237     }
2238 
2239     /**
2240      * Caches the state of the {@link PhoneAccount#CAPABILITY_VIDEO_CALLING} {@link PhoneAccount}
2241      * capability and ensures that the video state is updated if the phone account does not support
2242      * video calling.
2243      */
checkIfVideoCapable()2244     private void checkIfVideoCapable() {
2245         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
2246         if (mTargetPhoneAccountHandle == null) {
2247             // If no target phone account handle is specified, assume we can potentially perform a
2248             // video call; once the phone account is set, we can confirm that it is video capable.
2249             mIsVideoCallingSupportedByPhoneAccount = true;
2250             Log.d(this, "checkIfVideoCapable: no phone account selected; assume video capable.");
2251             return;
2252         }
2253         PhoneAccount phoneAccount =
2254                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
2255         mIsVideoCallingSupportedByPhoneAccount = phoneAccount != null &&
2256                 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING);
2257 
2258         if (!mIsVideoCallingSupportedByPhoneAccount && VideoProfile.isVideo(getVideoState())) {
2259             // The PhoneAccount for the Call was set to one which does not support video calling,
2260             // and the current call is configured to be a video call; downgrade to audio-only.
2261             setVideoState(VideoProfile.STATE_AUDIO_ONLY);
2262             Log.d(this, "checkIfVideoCapable: selected phone account doesn't support video.");
2263         }
2264     }
2265 
checkIfRttCapable()2266     private void checkIfRttCapable() {
2267         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
2268         if (mTargetPhoneAccountHandle == null) {
2269             return;
2270         }
2271 
2272         // Check both the target phone account and the connection manager phone account -- if
2273         // either support RTT, just set the streams and have them set/unset the RTT property as
2274         // needed.
2275         PhoneAccount phoneAccount =
2276                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
2277         PhoneAccount connectionManagerPhoneAccount = phoneAccountRegistrar.getPhoneAccountUnchecked(
2278                         mConnectionManagerPhoneAccountHandle);
2279         boolean isRttSupported = phoneAccount != null && phoneAccount.hasCapabilities(
2280                 PhoneAccount.CAPABILITY_RTT);
2281         boolean isConnectionManagerRttSupported = connectionManagerPhoneAccount != null
2282                 && connectionManagerPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT);
2283 
2284         if ((isConnectionManagerRttSupported || isRttSupported)
2285                 && mDidRequestToStartWithRtt && !areRttStreamsInitialized()) {
2286             // If the phone account got set to an RTT capable one and we haven't set the streams
2287             // yet, do so now.
2288             createRttStreams();
2289             Log.i(this, "Setting RTT streams after target phone account selected");
2290         }
2291     }
2292 
shouldAttachToExistingConnection()2293     boolean shouldAttachToExistingConnection() {
2294         return mShouldAttachToExistingConnection;
2295     }
2296 
2297     /**
2298      * Note: This method relies on {@link #mConnectElapsedTimeMillis} and
2299      * {@link #mDisconnectElapsedTimeMillis} which are independent of the wall clock (which could
2300      * change due to clock changes).
2301      * @return The "age" of this call object in milliseconds, which typically also represents the
2302      *     period since this call was added to the set pending outgoing calls.
2303      */
getAgeMillis()2304     public long getAgeMillis() {
2305         if (mState == CallState.DISCONNECTED &&
2306                 (mDisconnectCause.getCode() == DisconnectCause.REJECTED ||
2307                  mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
2308             // Rejected and missed calls have no age. They're immortal!!
2309             return 0;
2310         } else if (mConnectElapsedTimeMillis == 0) {
2311             // Age is measured in the amount of time the call was active. A zero connect time
2312             // indicates that we never went active, so return 0 for the age.
2313             return 0;
2314         } else if (mDisconnectElapsedTimeMillis == 0) {
2315             // We connected, but have not yet disconnected
2316             return mClockProxy.elapsedRealtime() - mConnectElapsedTimeMillis;
2317         }
2318 
2319         return mDisconnectElapsedTimeMillis - mConnectElapsedTimeMillis;
2320     }
2321 
2322     /**
2323      * @return The time when this call object was created and added to the set of pending outgoing
2324      *     calls.
2325      */
getCreationTimeMillis()2326     public long getCreationTimeMillis() {
2327         return mCreationTimeMillis;
2328     }
2329 
2330     /**
2331      * @return The elapsed realtime millis when the call was created; ONLY useful for determining
2332      * how long has elapsed since the call was first created.
2333      */
getCreationElapsedRealtimeMillis()2334     public long getCreationElapsedRealtimeMillis() {
2335         return mCreationElapsedRealtimeMillis;
2336     }
2337 
getConnectTimeMillis()2338     public long getConnectTimeMillis() {
2339         return mConnectTimeMillis;
2340     }
2341 
setConnectTimeMillis(long connectTimeMillis)2342     public void setConnectTimeMillis(long connectTimeMillis) {
2343         mConnectTimeMillis = connectTimeMillis;
2344     }
2345 
setConnectElapsedTimeMillis(long connectElapsedTimeMillis)2346     public void setConnectElapsedTimeMillis(long connectElapsedTimeMillis) {
2347         mConnectElapsedTimeMillis = connectElapsedTimeMillis;
2348     }
2349 
getConnectionCapabilities()2350     public int getConnectionCapabilities() {
2351         return stripUnsupportedCapabilities(mConnectionCapabilities);
2352     }
2353 
getConnectionProperties()2354     int getConnectionProperties() {
2355         return mConnectionProperties;
2356     }
2357 
setConnectionCapabilities(int connectionCapabilities)2358     public void setConnectionCapabilities(int connectionCapabilities) {
2359         setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */);
2360     }
2361 
setTransactionalCapabilities(Bundle extras)2362     public void setTransactionalCapabilities(Bundle extras) {
2363         if (!mFlags.remapTransactionalCapabilities()) {
2364             setConnectionCapabilities(
2365                     extras.getInt(CallAttributes.CALL_CAPABILITIES_KEY,
2366                             CallAttributes.SUPPORTS_SET_INACTIVE), true);
2367             return;
2368         }
2369         int connectionCapabilitesBitmap = 0;
2370         int transactionalCapabilitiesBitmap = extras.getInt(
2371                 CallAttributes.CALL_CAPABILITIES_KEY,
2372                 CallAttributes.SUPPORTS_SET_INACTIVE);
2373         if ((transactionalCapabilitiesBitmap & CallAttributes.SUPPORTS_SET_INACTIVE)
2374                 == CallAttributes.SUPPORTS_SET_INACTIVE) {
2375             connectionCapabilitesBitmap = connectionCapabilitesBitmap | Connection.CAPABILITY_HOLD
2376                     | Connection.CAPABILITY_SUPPORT_HOLD;
2377         }
2378         setConnectionCapabilities(connectionCapabilitesBitmap, true);
2379     }
2380 
setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate)2381     void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) {
2382         Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString(
2383                 connectionCapabilities));
2384         if (forceUpdate || mConnectionCapabilities != connectionCapabilities) {
2385             int previousCapabilities = mConnectionCapabilities;
2386             mConnectionCapabilities = connectionCapabilities;
2387             for (Listener l : mListeners) {
2388                 l.onConnectionCapabilitiesChanged(this);
2389             }
2390 
2391             int strippedCaps = getConnectionCapabilities();
2392             int xorCaps = previousCapabilities ^ strippedCaps;
2393             Log.addEvent(this, LogUtils.Events.CAPABILITY_CHANGE,
2394                     "Current: [%s], Removed [%s], Added [%s]",
2395                     Connection.capabilitiesToStringShort(strippedCaps),
2396                     Connection.capabilitiesToStringShort(previousCapabilities & xorCaps),
2397                     Connection.capabilitiesToStringShort(strippedCaps & xorCaps));
2398         }
2399     }
2400 
2401     /**
2402      * For some states of Telecom, we need to modify this connection's capabilities:
2403      * - A user should not be able to pull an external call during an emergency call, so
2404      *   CAPABILITY_CAN_PULL_CALL should be removed until the emergency call ends.
2405      * @param capabilities The original capabilities.
2406      * @return The stripped capabilities.
2407      */
stripUnsupportedCapabilities(int capabilities)2408     private int stripUnsupportedCapabilities(int capabilities) {
2409         if (!mIsPullExternalCallSupported) {
2410             if ((capabilities |= Connection.CAPABILITY_CAN_PULL_CALL) > 0) {
2411                 capabilities &= ~Connection.CAPABILITY_CAN_PULL_CALL;
2412                 Log.i(this, "stripCapabilitiesBasedOnState: CAPABILITY_CAN_PULL_CALL removed.");
2413             }
2414         }
2415         return capabilities;
2416     }
2417 
setConnectionProperties(int connectionProperties)2418     public void setConnectionProperties(int connectionProperties) {
2419         Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString(
2420                 connectionProperties));
2421 
2422         // Ensure the ConnectionService can't change the state of the self-managed property.
2423         if (isSelfManaged()) {
2424             connectionProperties |= Connection.PROPERTY_SELF_MANAGED;
2425         } else {
2426             connectionProperties &= ~Connection.PROPERTY_SELF_MANAGED;
2427         }
2428 
2429         int changedProperties = mConnectionProperties ^ connectionProperties;
2430 
2431         if (changedProperties != 0) {
2432             int previousProperties = mConnectionProperties;
2433             mConnectionProperties = connectionProperties;
2434             boolean didRttChange =
2435                     (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
2436             if (didRttChange) {
2437                 if ((mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
2438                         Connection.PROPERTY_IS_RTT) {
2439                     // If we already had RTT streams up, that means that either the call started
2440                     // with RTT or the user previously requested to start RTT. Either way, don't
2441                     // play the alert tone.
2442                     if (!areRttStreamsInitialized()) {
2443                         mCallsManager.playRttUpgradeToneForCall(this);
2444                     }
2445 
2446                     createRttStreams();
2447                     // Call startRtt to pass the RTT pipes down to the connection service.
2448                     // They already turned on the RTT property so no request should be sent.
2449                     if (mConnectionService != null) {
2450                         mConnectionService.startRtt(this,
2451                                 getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
2452                     }
2453                     mWasEverRtt = true;
2454                     if (isEmergencyCall()) {
2455                         mCallsManager.mute(false);
2456                     }
2457                 } else {
2458                     closeRttStreams();
2459                     mInCallToConnectionServiceStreams = null;
2460                     mConnectionServiceToInCallStreams = null;
2461                 }
2462             }
2463             mWasHighDefAudio = (connectionProperties & Connection.PROPERTY_HIGH_DEF_AUDIO) ==
2464                     Connection.PROPERTY_HIGH_DEF_AUDIO;
2465             mWasWifi = (connectionProperties & Connection.PROPERTY_WIFI) > 0;
2466             for (Listener l : mListeners) {
2467                 l.onConnectionPropertiesChanged(this, didRttChange);
2468             }
2469 
2470             boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
2471                     == Connection.PROPERTY_IS_EXTERNAL_CALL;
2472             boolean isExternal = (connectionProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
2473                     == Connection.PROPERTY_IS_EXTERNAL_CALL;
2474             if (wasExternal != isExternal) {
2475                 Log.v(this, "setConnectionProperties: external call changed isExternal = %b",
2476                         isExternal);
2477                 Log.addEvent(this, LogUtils.Events.IS_EXTERNAL, isExternal);
2478                 if (isExternal) {
2479                     // If there is an ongoing emergency call, remove the ability for this call to
2480                     // be pulled.
2481                     boolean isInEmergencyCall = mCallsManager.isInEmergencyCall();
2482                     setIsPullExternalCallSupported(!isInEmergencyCall);
2483                 }
2484                 for (Listener l : mListeners) {
2485                     l.onExternalCallChanged(this, isExternal);
2486                 }
2487             }
2488 
2489             boolean wasDowngradedConference =
2490                     (previousProperties & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0;
2491             boolean isDowngradedConference =
2492                     (connectionProperties & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0;
2493             if (wasDowngradedConference && !isDowngradedConference) {
2494                 Log.i(this, "DOWNGRADED_CONFERENCE property removed; setting"
2495                         + " conference state to false");
2496                 setConferenceState(false);
2497             }
2498 
2499             mAnalytics.addCallProperties(mConnectionProperties);
2500 
2501             int xorProps = previousProperties ^ mConnectionProperties;
2502             Log.addEvent(this, LogUtils.Events.PROPERTY_CHANGE,
2503                     "Current: [%s], Removed [%s], Added [%s]",
2504                     Connection.propertiesToStringShort(mConnectionProperties),
2505                     Connection.propertiesToStringShort(previousProperties & xorProps),
2506                     Connection.propertiesToStringShort(mConnectionProperties & xorProps));
2507         }
2508     }
2509 
getSupportedAudioRoutes()2510     public int getSupportedAudioRoutes() {
2511         return mSupportedAudioRoutes;
2512     }
2513 
setSupportedAudioRoutes(int audioRoutes)2514     void setSupportedAudioRoutes(int audioRoutes) {
2515         if (mSupportedAudioRoutes != audioRoutes) {
2516             mSupportedAudioRoutes = audioRoutes;
2517         }
2518     }
2519 
2520     @VisibleForTesting
getParentCall()2521     public Call getParentCall() {
2522         return mParentCall;
2523     }
2524 
2525     @VisibleForTesting
getChildCalls()2526     public List<Call> getChildCalls() {
2527         return mChildCalls;
2528     }
2529 
2530     @VisibleForTesting
wasConferencePreviouslyMerged()2531     public boolean wasConferencePreviouslyMerged() {
2532         return mWasConferencePreviouslyMerged;
2533     }
2534 
isDisconnectingChildCall()2535     public boolean isDisconnectingChildCall() {
2536         return mIsDisconnectingChildCall;
2537     }
2538 
2539     /**
2540      * Sets whether this call is a child call.
2541      */
maybeSetCallAsDisconnectingChild()2542     private void maybeSetCallAsDisconnectingChild() {
2543         if (mParentCall != null) {
2544             mIsDisconnectingChildCall = true;
2545         }
2546     }
2547 
2548     @VisibleForTesting
getConferenceLevelActiveCall()2549     public Call getConferenceLevelActiveCall() {
2550         return mConferenceLevelActiveCall;
2551     }
2552 
getConnectionService()2553     public ConnectionServiceWrapper getConnectionService() {
2554         return mConnectionService;
2555     }
2556 
2557     /**
2558      * Retrieves the {@link Context} for the call.
2559      *
2560      * @return The {@link Context}.
2561      */
getContext()2562     public Context getContext() {
2563         return mContext;
2564     }
2565 
2566     @VisibleForTesting
setConnectionService(ConnectionServiceWrapper service)2567     public void setConnectionService(ConnectionServiceWrapper service) {
2568         Log.i(this, "setConnectionService: service=[%s]", service);
2569         setConnectionService(service, null);
2570     }
2571 
2572     @VisibleForTesting
setConnectionService( ConnectionServiceWrapper service, ConnectionServiceWrapper remoteService )2573     public void setConnectionService(
2574             ConnectionServiceWrapper service,
2575             ConnectionServiceWrapper remoteService
2576     ) {
2577         Preconditions.checkNotNull(service);
2578 
2579         clearConnectionService();
2580 
2581         service.incrementAssociatedCallCount();
2582 
2583         if (mFlags.updatedRcsCallCountTracking() && remoteService != null) {
2584             remoteService.incrementAssociatedCallCount();
2585             mRemoteConnectionService = remoteService;
2586         }
2587 
2588         mConnectionService = service;
2589         mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
2590         mConnectionService.addCall(this);
2591         processCachedCallbacks(service);
2592     }
2593 
2594     /**
2595      * Perform an in-place replacement of the {@link ConnectionServiceWrapper} for this Call.
2596      * Removes the call from its former {@link ConnectionServiceWrapper}, while still ensuring the
2597      * former {@link ConnectionServiceWrapper} is tracked as the mRemoteConnectionService for this
2598      * call so that the associatedCallCount of that {@link ConnectionServiceWrapper} is accurately
2599      * tracked until it is supposed to be unbound.
2600      * This method is used by the {@link ConnectionServiceWrapper} when handling {@link Connection}
2601      * and {@link Conference} additions via a ConnectionManager.
2602      * The original {@link android.telecom.ConnectionService} will directly add external calls and
2603      * conferences to Telecom as well as the ConnectionManager, which will add to Telecom.  In these
2604      * cases since its first added to via the original CS, we want to change the CS responsible for
2605      * the call to the ConnectionManager rather than adding it again as another call/conference.
2606      *
2607      * @param service The new {@link ConnectionServiceWrapper}.
2608      */
replaceConnectionService(ConnectionServiceWrapper service)2609     public void replaceConnectionService(ConnectionServiceWrapper service) {
2610         Preconditions.checkNotNull(service);
2611 
2612         if (mConnectionService != null) {
2613             ConnectionServiceWrapper serviceTemp = mConnectionService;
2614 
2615             if (mFlags.updatedRcsCallCountTracking()) {
2616                 // Continue to track the former CS for this call so that it doesn't unbind early:
2617                 mRemoteConnectionService = serviceTemp;
2618             }
2619 
2620             mConnectionService = null;
2621             serviceTemp.removeCall(this);
2622 
2623             if (!mFlags.updatedRcsCallCountTracking()) {
2624                 serviceTemp.decrementAssociatedCallCount(true /*isSuppressingUnbind*/);
2625             }
2626         }
2627 
2628         service.incrementAssociatedCallCount();
2629         mConnectionService = service;
2630         mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
2631     }
2632 
2633     /**
2634      * Clears the associated connection service.
2635      */
clearConnectionService()2636     void clearConnectionService() {
2637         if (mConnectionService != null) {
2638             ConnectionServiceWrapper serviceTemp = mConnectionService;
2639             ConnectionServiceWrapper remoteServiceTemp = mRemoteConnectionService;
2640             mRemoteConnectionService = null;
2641             mConnectionService = null;
2642             serviceTemp.removeCall(this);
2643 
2644             // Decrementing the count can cause the service to unbind, which itself can trigger the
2645             // service-death code.  Since the service death code tries to clean up any associated
2646             // calls, we need to make sure to remove that information (e.g., removeCall()) before
2647             // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
2648             // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
2649             // to do.
2650             decrementAssociatedCallCount(serviceTemp);
2651 
2652             if (mFlags.updatedRcsCallCountTracking() && remoteServiceTemp != null) {
2653                 decrementAssociatedCallCount(remoteServiceTemp);
2654             }
2655         }
2656     }
2657 
2658     /**
2659      * Starts the create connection sequence. Upon completion, there should exist an active
2660      * connection through a connection service (or the call will have failed).
2661      *
2662      * @param phoneAccountRegistrar The phone account registrar.
2663      */
startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar)2664     void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
2665         if (mCreateConnectionProcessor != null) {
2666             Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
2667                     " due to a race between NewOutgoingCallIntentBroadcaster and " +
2668                     "phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
2669                     "invocation.");
2670             return;
2671         }
2672         mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
2673                 phoneAccountRegistrar, mCallsManager, mContext, mFlags, new Timeouts.Adapter());
2674         mCreateConnectionProcessor.process();
2675     }
2676 
2677     @Override
handleCreateConferenceSuccess( CallIdMapper idMapper, ParcelableConference conference)2678     public void handleCreateConferenceSuccess(
2679             CallIdMapper idMapper,
2680             ParcelableConference conference) {
2681         Log.v(this, "handleCreateConferenceSuccessful %s", conference);
2682         mIsCreateConnectionComplete = true;
2683         setTargetPhoneAccount(conference.getPhoneAccount());
2684         setHandle(conference.getHandle(), conference.getHandlePresentation());
2685 
2686         setConnectionCapabilities(conference.getConnectionCapabilities());
2687         setConnectionProperties(conference.getConnectionProperties());
2688         setVideoProvider(conference.getVideoProvider());
2689         setVideoState(conference.getVideoState());
2690         setRingbackRequested(conference.isRingbackRequested());
2691         setStatusHints(conference.getStatusHints());
2692         putConnectionServiceExtras(conference.getExtras());
2693 
2694         switch (mCallDirection) {
2695             case CALL_DIRECTION_INCOMING:
2696                 // Listeners (just CallsManager for now) will be responsible for checking whether
2697                 // the call should be blocked.
2698                 for (Listener l : mListeners) {
2699                     l.onSuccessfulIncomingCall(this);
2700                 }
2701                 break;
2702             case CALL_DIRECTION_OUTGOING:
2703                 for (Listener l : mListeners) {
2704                     l.onSuccessfulOutgoingCall(this,
2705                             getStateFromConnectionState(conference.getState()));
2706                 }
2707                 break;
2708         }
2709     }
2710 
2711     @Override
handleCreateConnectionSuccess( CallIdMapper idMapper, ParcelableConnection connection)2712     public void handleCreateConnectionSuccess(
2713             CallIdMapper idMapper,
2714             ParcelableConnection connection) {
2715         Log.v(this, "handleCreateConnectionSuccessful %s", connection);
2716         mIsCreateConnectionComplete = true;
2717         setTargetPhoneAccount(connection.getPhoneAccount());
2718         setHandle(connection.getHandle(), connection.getHandlePresentation());
2719 
2720         setCallerDisplayName(
2721                 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
2722         setConnectionCapabilities(connection.getConnectionCapabilities());
2723         setConnectionProperties(connection.getConnectionProperties());
2724         setIsVoipAudioMode(connection.getIsVoipAudioMode());
2725         setSupportedAudioRoutes(connection.getSupportedAudioRoutes());
2726         setVideoProvider(connection.getVideoProvider());
2727         setVideoState(connection.getVideoState());
2728         setRingbackRequested(connection.isRingbackRequested());
2729         setStatusHints(connection.getStatusHints());
2730         putConnectionServiceExtras(connection.getExtras());
2731 
2732         mConferenceableCalls.clear();
2733         for (String id : connection.getConferenceableConnectionIds()) {
2734             mConferenceableCalls.add(idMapper.getCall(id));
2735         }
2736 
2737         switch (mCallDirection) {
2738             case CALL_DIRECTION_INCOMING:
2739                 setCallerNumberVerificationStatus(connection.getCallerNumberVerificationStatus());
2740 
2741                 // Listeners (just CallsManager for now) will be responsible for checking whether
2742                 // the call should be blocked.
2743                 for (Listener l : mListeners) {
2744                     l.onSuccessfulIncomingCall(this);
2745                 }
2746                 break;
2747             case CALL_DIRECTION_OUTGOING:
2748                 for (Listener l : mListeners) {
2749                     l.onSuccessfulOutgoingCall(this,
2750                             getStateFromConnectionState(connection.getState()));
2751                 }
2752                 break;
2753             case CALL_DIRECTION_UNKNOWN:
2754                 for (Listener l : mListeners) {
2755                     l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection
2756                             .getState()));
2757                 }
2758                 break;
2759         }
2760     }
2761 
2762     @Override
handleCreateConferenceFailure(DisconnectCause disconnectCause)2763     public void handleCreateConferenceFailure(DisconnectCause disconnectCause) {
2764         Log.i(this, "handleCreateConferenceFailure; callid=%s, disconnectCause=%s",
2765                 getId(), disconnectCause);
2766         clearConnectionService();
2767         setDisconnectCause(disconnectCause);
2768         mCallsManager.markCallAsDisconnected(this, disconnectCause);
2769 
2770         switch (mCallDirection) {
2771             case CALL_DIRECTION_INCOMING:
2772                 for (Listener listener : mListeners) {
2773                     listener.onFailedIncomingCall(this);
2774                 }
2775                 break;
2776             case CALL_DIRECTION_OUTGOING:
2777                 for (Listener listener : mListeners) {
2778                     listener.onFailedOutgoingCall(this, disconnectCause);
2779                 }
2780                 break;
2781         }
2782     }
2783 
2784     @Override
handleCreateConnectionFailure(DisconnectCause disconnectCause)2785     public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
2786         Log.i(this, "handleCreateConnectionFailure; callid=%s, disconnectCause=%s",
2787                 getId(), disconnectCause);
2788         clearConnectionService();
2789         setDisconnectCause(disconnectCause);
2790         mCallsManager.markCallAsDisconnected(this, disconnectCause);
2791 
2792         switch (mCallDirection) {
2793             case CALL_DIRECTION_INCOMING:
2794                 for (Listener listener : mListeners) {
2795                     listener.onFailedIncomingCall(this);
2796                 }
2797                 break;
2798             case CALL_DIRECTION_OUTGOING:
2799                 for (Listener listener : mListeners) {
2800                     listener.onFailedOutgoingCall(this, disconnectCause);
2801                 }
2802                 break;
2803             case CALL_DIRECTION_UNKNOWN:
2804                 for (Listener listener : mListeners) {
2805                     listener.onFailedUnknownCall(this);
2806                 }
2807                 break;
2808         }
2809     }
2810 
2811     /**
2812      * Plays the specified DTMF tone.
2813      */
2814     @VisibleForTesting
playDtmfTone(char digit)2815     public void playDtmfTone(char digit) {
2816         if (mConnectionService == null) {
2817             Log.w(this, "playDtmfTone() request on a call without a connection service.");
2818         } else {
2819             Log.i(this, "Send playDtmfTone to connection service for call %s", this);
2820             mConnectionService.playDtmfTone(this, digit);
2821             Log.addEvent(this, LogUtils.Events.START_DTMF, Log.pii(digit));
2822         }
2823         mPlayingDtmfTone = digit;
2824     }
2825 
2826     /**
2827      * Stops playing any currently playing DTMF tone.
2828      */
2829     @VisibleForTesting
stopDtmfTone()2830     public void stopDtmfTone() {
2831         if (mConnectionService == null) {
2832             Log.w(this, "stopDtmfTone() request on a call without a connection service.");
2833         } else {
2834             Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
2835             Log.addEvent(this, LogUtils.Events.STOP_DTMF);
2836             mConnectionService.stopDtmfTone(this);
2837         }
2838         mPlayingDtmfTone = NO_DTMF_TONE;
2839     }
2840 
2841     /**
2842      * @return {@code true} if a DTMF tone has been started via {@link #playDtmfTone(char)} but has
2843      * not been stopped via {@link #stopDtmfTone()}, {@code false} otherwise.
2844      */
isDtmfTonePlaying()2845     boolean isDtmfTonePlaying() {
2846         return mPlayingDtmfTone != NO_DTMF_TONE;
2847     }
2848 
2849     /**
2850      * Silences the ringer.
2851      */
silence()2852     void silence() {
2853         if (mConnectionService == null) {
2854             Log.w(this, "silence() request on a call without a connection service.");
2855         } else {
2856             Log.i(this, "Send silence to connection service for call %s", this);
2857             Log.addEvent(this, LogUtils.Events.SILENCE);
2858             mConnectionService.silence(this);
2859         }
2860     }
2861 
2862     @VisibleForTesting
disconnect()2863     public CompletableFuture<Boolean> disconnect() {
2864         return disconnect(0);
2865     }
2866 
disconnect(String reason)2867     public CompletableFuture<Boolean> disconnect(String reason) {
2868         return disconnect(0, reason);
2869     }
2870 
2871     /**
2872      * Attempts to disconnect the call through the connection service.
2873      */
2874     @VisibleForTesting
disconnect(long disconnectionTimeout)2875     public CompletableFuture<Boolean> disconnect(long disconnectionTimeout) {
2876         return disconnect(disconnectionTimeout, "internal" /* reason */);
2877     }
2878 
2879     /**
2880      * Attempts to disconnect the call through the connection service.
2881      * @param reason the reason for the disconnect; used for logging purposes only.  In some cases
2882      *               this can be a package name if the disconnect was initiated through an API such
2883      *               as TelecomManager.
2884      */
2885     @VisibleForTesting
disconnect(long disconnectionTimeout, String reason)2886     public CompletableFuture<Boolean> disconnect(long disconnectionTimeout,
2887             String reason) {
2888         Log.addEvent(this, LogUtils.Events.REQUEST_DISCONNECT, reason);
2889 
2890         // Track that the call is now locally disconnecting.
2891         setLocallyDisconnecting(true);
2892         maybeSetCallAsDisconnectingChild();
2893 
2894         CompletableFuture<Boolean> disconnectFutureHandler =
2895                 CompletableFuture.completedFuture(false);
2896         if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||
2897                 mState == CallState.CONNECTING) {
2898             Log.i(this, "disconnect: Aborting call %s", getId());
2899             if (mFlags.enableCallSequencing()) {
2900                 disconnectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall(
2901                         false /* shouldDisconnectUponTimeout */, "disconnect",
2902                         CallState.DISCONNECTED, CallState.ABORTED);
2903             }
2904             abort(disconnectionTimeout);
2905         } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
2906             if (mState == CallState.AUDIO_PROCESSING && !hasGoneActiveBefore()) {
2907                 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
2908             } else if (mState == CallState.SIMULATED_RINGING) {
2909                 // This is the case where the dialer calls disconnect() because the call timed out
2910                 // or an emergency call was dialed while in this state.
2911                 // Override the disconnect cause to MISSED
2912                 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.MISSED));
2913             }
2914             if (mTransactionalService != null) {
2915                 disconnectFutureHandler = mTransactionalService.onDisconnect(this,
2916                         getDisconnectCause());
2917                 Log.i(this, "Send Disconnect to transactional service for call");
2918             } else if (mConnectionService == null) {
2919                 Log.e(this, new Exception(), "disconnect() request on a call without a"
2920                         + " connection service.");
2921             } else {
2922                 Log.i(this, "Send disconnect to connection service for call: %s", this);
2923                 // The call isn't officially disconnected until the connection service
2924                 // confirms that the call was actually disconnected. Only then is the
2925                 // association between call and connection service severed, see
2926                 // {@link CallsManager#markCallAsDisconnected}.
2927                 if (mFlags.enableCallSequencing()) {
2928                     disconnectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall(
2929                             false /* shouldDisconnectUponTimeout */, "disconnect",
2930                             CallState.DISCONNECTED);
2931                 }
2932                 mConnectionService.disconnect(this);
2933             }
2934         }
2935         return disconnectFutureHandler;
2936     }
2937 
abort(long disconnectionTimeout)2938     void abort(long disconnectionTimeout) {
2939         if (mCreateConnectionProcessor != null &&
2940                 !mCreateConnectionProcessor.isProcessingComplete()) {
2941             mCreateConnectionProcessor.abort();
2942         } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT
2943                 || mState == CallState.CONNECTING) {
2944             if (disconnectionTimeout > 0) {
2945                 // If the cancelation was from NEW_OUTGOING_CALL with a timeout of > 0
2946                 // milliseconds, do not destroy the call.
2947                 // Instead, we announce the cancellation and CallsManager handles
2948                 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and
2949                 // then re-dial them quickly using a gateway, allowing the first call to end
2950                 // causes jank. This timeout allows CallsManager to transition the first call into
2951                 // the second call so that in-call only ever sees a single call...eliminating the
2952                 // jank altogether. The app will also be able to set the timeout via an extra on
2953                 // the ordered broadcast.
2954                 for (Listener listener : mListeners) {
2955                     if (listener.onCanceledViaNewOutgoingCallBroadcast(
2956                             this, disconnectionTimeout)) {
2957                         // The first listener to handle this wins. A return value of true means that
2958                         // the listener will handle the disconnection process later and so we
2959                         // should not continue it here.
2960                         setLocallyDisconnecting(false);
2961                         return;
2962                     }
2963                 }
2964             }
2965 
2966             handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
2967         } else {
2968             Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING");
2969         }
2970     }
2971 
2972     /**
2973      * Answers the call if it is ringing.
2974      *
2975      * @param videoState The video state in which to answer the call.
2976      */
2977     @VisibleForTesting
answer(int videoState)2978     public CompletableFuture<Boolean> answer(int videoState) {
2979         CompletableFuture<Boolean> answerCallFuture = CompletableFuture.completedFuture(false);
2980         // Check to verify that the call is still in the ringing state. A call can change states
2981         // between the time the user hits 'answer' and Telecom receives the command.
2982         if (isRinging("answer")) {
2983             Log.addEvent(this, LogUtils.Events.REQUEST_ACCEPT);
2984             if (!isVideoCallingSupportedByPhoneAccount() && VideoProfile.isVideo(videoState)) {
2985                 // Video calling is not supported, yet the InCallService is attempting to answer as
2986                 // video.  We will simply answer as audio-only.
2987                 videoState = VideoProfile.STATE_AUDIO_ONLY;
2988             }
2989             // At this point, we are asking the connection service to answer but we don't assume
2990             // that it will work. Instead, we wait until confirmation from the connection service
2991             // that the call is in a non-STATE_RINGING state before changing the UI. See
2992             // {@link ConnectionServiceAdapter#setActive} and other set* methods.
2993             if (mConnectionService != null) {
2994                 if (mFlags.enableCallSequencing()) {
2995                     answerCallFuture = awaitCallStateChangeAndMaybeDisconnectCall(
2996                             false /* shouldDisconnectUponTimeout */, "answer", CallState.ACTIVE);
2997                 }
2998                 mConnectionService.answer(this, videoState);
2999             } else if (mTransactionalService != null) {
3000                 return mTransactionalService.onAnswer(this, videoState);
3001             } else {
3002                 Log.e(this, new NullPointerException(),
3003                         "answer call failed due to null CS callId=%s", getId());
3004             }
3005         }
3006         return answerCallFuture;
3007     }
3008 
3009     /**
3010      * Answers the call on the connectionservice side in order to start audio processing.
3011      *
3012      * This pathway keeps the call in the ANSWERED state until the connection service confirms the
3013      * answer, at which point we'll set it to AUDIO_PROCESSING. However, to prevent any other
3014      * components from seeing the churn between RINGING -> ANSWERED -> AUDIO_PROCESSING, we'll
3015      * refrain from tracking this call in CallsManager until we've stabilized in AUDIO_PROCESSING
3016      */
answerForAudioProcessing()3017     public void answerForAudioProcessing() {
3018         if (mState != CallState.RINGING) {
3019             Log.w(this, "Trying to audio-process a non-ringing call: id=%s", mId);
3020             return;
3021         }
3022 
3023         if (mConnectionService != null) {
3024             mConnectionService.answer(this, VideoProfile.STATE_AUDIO_ONLY);
3025         } else {
3026             Log.e(this, new NullPointerException(),
3027                     "answer call (audio processing) failed due to null CS callId=%s", getId());
3028         }
3029 
3030         Log.addEvent(this, LogUtils.Events.REQUEST_PICKUP_FOR_AUDIO_PROCESSING);
3031     }
3032 
setAudioProcessingRequestingApp(CharSequence appName)3033     public void setAudioProcessingRequestingApp(CharSequence appName) {
3034         mAudioProcessingRequestingApp = appName;
3035     }
3036 
getAudioProcessingRequestingApp()3037     public CharSequence getAudioProcessingRequestingApp() {
3038         return mAudioProcessingRequestingApp;
3039     }
3040 
3041     /**
3042      * Deflects the call if it is ringing.
3043      *
3044      * @param address address to be deflected to.
3045      */
3046     @VisibleForTesting
deflect(Uri address)3047     public void deflect(Uri address) {
3048         // Check to verify that the call is still in the ringing state. A call can change states
3049         // between the time the user hits 'deflect' and Telecomm receives the command.
3050         if (isRinging("deflect")) {
3051             // At this point, we are asking the connection service to deflect but we don't assume
3052             // that it will work. Instead, we wait until confirmation from the connection service
3053             // that the call is in a non-STATE_RINGING state before changing the UI. See
3054             // {@link ConnectionServiceAdapter#setActive} and other set* methods.
3055             mVideoStateHistory |= mVideoState;
3056             if (mConnectionService != null) {
3057                 mConnectionService.deflect(this, address);
3058             } else {
3059                 Log.e(this, new NullPointerException(),
3060                         "deflect call failed due to null CS callId=%s", getId());
3061             }
3062             Log.addEvent(this, LogUtils.Events.REQUEST_DEFLECT, Log.pii(address));
3063         }
3064     }
3065 
3066     /**
3067      * Rejects the call if it is ringing.
3068      *
3069      * @param rejectWithMessage Whether to send a text message as part of the call rejection.
3070      * @param textMessage An optional text message to send as part of the rejection.
3071      */
3072     @VisibleForTesting
reject(boolean rejectWithMessage, String textMessage)3073     public void reject(boolean rejectWithMessage, String textMessage) {
3074         reject(rejectWithMessage, textMessage, "internal" /** reason */);
3075     }
3076 
3077     /**
3078      * Rejects the call if it is ringing.
3079      *
3080      * @param rejectWithMessage Whether to send a text message as part of the call rejection.
3081      * @param textMessage An optional text message to send as part of the rejection.
3082      * @param reason The reason for the reject; used for logging purposes.  May be a package name
3083      *               if the reject is initiated from an API such as TelecomManager.
3084      */
3085     @VisibleForTesting
reject(boolean rejectWithMessage, String textMessage, String reason)3086     public CompletableFuture<Boolean> reject(boolean rejectWithMessage,
3087             String textMessage, String reason) {
3088         CompletableFuture<Boolean> rejectFutureHandler = CompletableFuture.completedFuture(false);
3089         if (mState == CallState.SIMULATED_RINGING) {
3090             Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason);
3091             // This handles the case where the user manually rejects a call that's in simulated
3092             // ringing. Since the call is already active on the connectionservice side, we want to
3093             // hangup, not reject.
3094             setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
3095             if (mTransactionalService != null) {
3096                 return mTransactionalService.onDisconnect(this,
3097                         new DisconnectCause(DisconnectCause.REJECTED));
3098             } else if (mConnectionService != null) {
3099                 if (mFlags.enableCallSequencing()) {
3100                     rejectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall(
3101                             false /* shouldDisconnectUponTimeout */, "reject",
3102                             CallState.DISCONNECTED);
3103                 }
3104                 mConnectionService.disconnect(this);
3105                 return rejectFutureHandler;
3106             } else {
3107                 Log.e(this, new NullPointerException(),
3108                         "reject call failed due to null CS callId=%s", getId());
3109             }
3110         } else if (isRinging("reject") || isAnswered("reject")) {
3111             Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason);
3112             // Ensure video state history tracks video state at time of rejection.
3113             mVideoStateHistory |= mVideoState;
3114 
3115             if (mTransactionalService != null) {
3116                 return mTransactionalService.onDisconnect(this,
3117                         new DisconnectCause(DisconnectCause.REJECTED));
3118             } else if (mConnectionService != null) {
3119                 if (mFlags.enableCallSequencing()) {
3120                     rejectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall(
3121                             false /* shouldDisconnectUponTimeout */, "reject",
3122                             CallState.DISCONNECTED);
3123                 }
3124                 mConnectionService.reject(this, rejectWithMessage, textMessage);
3125                 return rejectFutureHandler;
3126             } else {
3127                 Log.e(this, new NullPointerException(),
3128                         "reject call failed due to null CS callId=%s", getId());
3129             }
3130         }
3131         return rejectFutureHandler;
3132     }
3133 
3134     /**
3135      * Reject this Telecom call with the user-indicated reason.
3136      * @param rejectReason The user-indicated reason fore rejecting the call.
3137      */
reject(@ndroid.telecom.Call.RejectReason int rejectReason)3138     public CompletableFuture<Boolean> reject(@android.telecom.Call.RejectReason int rejectReason) {
3139         CompletableFuture<Boolean> rejectFutureHandler = CompletableFuture.completedFuture(false);
3140         if (mState == CallState.SIMULATED_RINGING) {
3141             Log.addEvent(this, LogUtils.Events.REQUEST_REJECT);
3142             // This handles the case where the user manually rejects a call that's in simulated
3143             // ringing. Since the call is already active on the connectionservice side, we want to
3144             // hangup, not reject.
3145             // Since its simulated reason we can't pass along the reject reason.
3146             setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
3147             if (mTransactionalService != null) {
3148                 return mTransactionalService.onDisconnect(this,
3149                         new DisconnectCause(DisconnectCause.REJECTED));
3150             } else if (mConnectionService != null) {
3151                 if (mFlags.enableCallSequencing()) {
3152                     rejectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall(
3153                             false /* shouldDisconnectUponTimeout */, "reject",
3154                             CallState.DISCONNECTED);
3155                 }
3156                 mConnectionService.disconnect(this);
3157             } else {
3158                 Log.e(this, new NullPointerException(),
3159                         "reject call failed due to null CS callId=%s", getId());
3160             }
3161         } else if (isRinging("reject") || isAnswered("reject")) {
3162             Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, rejectReason);
3163             // Ensure video state history tracks video state at time of rejection.
3164             mVideoStateHistory |= mVideoState;
3165             if (mTransactionalService != null) {
3166                 return mTransactionalService.onDisconnect(this,
3167                         new DisconnectCause(DisconnectCause.REJECTED));
3168             } else if (mConnectionService != null) {
3169                 if (mFlags.enableCallSequencing()) {
3170                     rejectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall(
3171                             false /* shouldDisconnectUponTimeout */, "reject",
3172                             CallState.DISCONNECTED);
3173                 }
3174                 mConnectionService.rejectWithReason(this, rejectReason);
3175             } else {
3176                 Log.e(this, new NullPointerException(),
3177                         "reject call failed due to null CS callId=%s", getId());
3178             }
3179         }
3180         return rejectFutureHandler;
3181     }
3182 
3183     /**
3184      * Transfers the call if it is active or held.
3185      *
3186      * @param number number to be transferred to.
3187      * @param isConfirmationRequired whether for blind or assured transfer.
3188      */
3189     @VisibleForTesting
transfer(Uri number, boolean isConfirmationRequired)3190     public void transfer(Uri number, boolean isConfirmationRequired) {
3191         if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
3192             if (mTransactionalService != null) {
3193                 Log.i(this, "transfer: called on TransactionalService. doing nothing");
3194             } else if (mConnectionService != null) {
3195                 mConnectionService.transfer(this, number, isConfirmationRequired);
3196             } else {
3197                 Log.e(this, new NullPointerException(),
3198                         "transfer call failed due to null CS callId=%s", getId());
3199             }
3200             Log.addEvent(this, LogUtils.Events.REQUEST_TRANSFER, Log.pii(number));
3201         }
3202     }
3203 
3204     /**
3205      * Transfers the call when this call is active and the other call is held.
3206      * This is for Consultative call transfer.
3207      *
3208      * @param otherCall The other {@link Call} to which this call will be transferred.
3209      */
3210     @VisibleForTesting
transfer(Call otherCall)3211     public void transfer(Call otherCall) {
3212         if (mState == CallState.ACTIVE &&
3213                 (otherCall != null && otherCall.getState() == CallState.ON_HOLD)) {
3214             if (mTransactionalService != null) {
3215                 Log.i(this, "transfer: called on TransactionalService. doing nothing");
3216             } else if (mConnectionService != null) {
3217                 mConnectionService.transfer(this, otherCall);
3218             } else {
3219                 Log.e(this, new NullPointerException(),
3220                         "transfer call failed due to null CS callId=%s", getId());
3221             }
3222             Log.addEvent(this, LogUtils.Events.REQUEST_CONSULTATIVE_TRANSFER, otherCall);
3223         }
3224     }
3225 
3226     /**
3227      * Puts the call on hold if it is currently active.
3228      */
3229     @VisibleForTesting
hold()3230     public CompletableFuture<Boolean> hold() {
3231         return hold(null /* reason */);
3232     }
3233 
3234     /**
3235      * This method requests the ConnectionService or TransactionalService hosting the call to put
3236      * the call on hold
3237      */
hold(String reason)3238     public CompletableFuture<Boolean> hold(String reason) {
3239         CompletableFuture<Boolean> holdFutureHandler = CompletableFuture.completedFuture(false);
3240         if (mState == CallState.ACTIVE) {
3241             Log.addEvent(this, LogUtils.Events.REQUEST_HOLD, reason);
3242             if (mTransactionalService != null) {
3243                 return mTransactionalService.onSetInactive(this);
3244             } else if (mConnectionService != null) {
3245                 if (mFlags.transactionalCsVerifier() || mFlags.enableCallSequencing()) {
3246                     holdFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall(isSelfManaged(),
3247                             "hold", CallState.ON_HOLD, CallState.DISCONNECTED).thenCompose(
3248                                     (result) -> {
3249                                         // Explicitly handle self-managed hold failures where we
3250                                         // explicitly disconnect the call and treat it as a
3251                                         // completed transaction.
3252                                         if (!result && isSelfManaged()) {
3253                                             Log.i(this, "hold: Completing transaction "
3254                                                     + "after disconnecting held call.");
3255                                             return CompletableFuture.completedFuture(true);
3256                                         }
3257                                         return CompletableFuture.completedFuture(result);
3258                                     });;
3259                 }
3260                 mConnectionService.hold(this);
3261                 return holdFutureHandler;
3262             } else {
3263                 Log.e(this, new NullPointerException(),
3264                         "hold call failed due to null CS callId=%s", getId());
3265             }
3266         }
3267         return holdFutureHandler;
3268     }
3269 
3270     /**
3271      * helper that can be used for any callback that requests a call state change and wants to
3272      * verify the change
3273      */
awaitCallStateChangeAndMaybeDisconnectCall( boolean shouldDisconnectUponTimeout, String callingMethod, int... targetCallStates)3274     public CompletableFuture<Boolean> awaitCallStateChangeAndMaybeDisconnectCall(
3275             boolean shouldDisconnectUponTimeout, String callingMethod, int... targetCallStates) {
3276         TransactionManager tm = TransactionManager.getInstance();
3277         CallTransaction callTransaction = new VerifyCallStateChangeTransaction(
3278                 mCallsManager.getLock(), this, targetCallStates);
3279         return tm.addTransaction(callTransaction,
3280                 new OutcomeReceiver<>() {
3281             @Override
3282             public void onResult(CallTransactionResult result) {
3283                 Log.i(this, "awaitCallStateChangeAndMaybeDisconnectCall: %s: onResult:"
3284                         + " due to CallException=[%s]", callingMethod, result);
3285             }
3286 
3287             @Override
3288             public void onError(CallException e) {
3289                 Log.i(this, "awaitCallStateChangeAndMaybeDisconnectCall: %s: onError"
3290                         + " due to CallException=[%s]", callingMethod, e);
3291                 if (shouldDisconnectUponTimeout) {
3292                     mCallsManager.markCallAsDisconnected(Call.this,
3293                             new DisconnectCause(DisconnectCause.ERROR,
3294                                     "did not hold in timeout window"));
3295                     mCallsManager.markCallAsRemoved(Call.this);
3296                 }
3297             }
3298         });
3299     }
3300 
3301     /**
3302      * Releases the call from hold if it is currently active.
3303      */
3304     @VisibleForTesting
3305     public CompletableFuture<Boolean> unhold() {
3306         return unhold(null /* reason */);
3307     }
3308 
3309     public CompletableFuture<Boolean> unhold(String reason) {
3310         CompletableFuture<Boolean> unholdFutureHandler = CompletableFuture.completedFuture(false);
3311         if (mState == CallState.ON_HOLD) {
3312             Log.addEvent(this, LogUtils.Events.REQUEST_UNHOLD, reason);
3313             if (mTransactionalService != null){
3314                 return mTransactionalService.onSetActive(this);
3315             } else if (mConnectionService != null){
3316                 if (mFlags.enableCallSequencing()) {
3317                     unholdFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall(
3318                             false /* shouldDisconnectUponTimeout */, "unhold", CallState.ACTIVE);
3319                 }
3320                 mConnectionService.unhold(this);
3321                 return unholdFutureHandler;
3322             } else {
3323                 Log.e(this, new NullPointerException(),
3324                         "unhold call failed due to null CS callId=%s", getId());
3325             }
3326         }
3327         return unholdFutureHandler;
3328     }
3329 
3330     /** Checks if this is a live call or not. */
3331     @VisibleForTesting
3332     public boolean isAlive() {
3333         switch (mState) {
3334             case CallState.NEW:
3335             case CallState.RINGING:
3336             case CallState.ANSWERED:
3337             case CallState.DISCONNECTED:
3338             case CallState.ABORTED:
3339                 return false;
3340             default:
3341                 return true;
3342         }
3343     }
3344 
3345     public boolean isActive() {
3346         return mState == CallState.ACTIVE;
3347     }
3348 
3349     @VisibleForTesting
3350     public Bundle getExtras() {
3351         return mExtras;
3352     }
3353 
3354     /**
3355      * Adds extras to the extras bundle associated with this {@link Call}, as made by a
3356      * {@link ConnectionService} or other non {@link android.telecom.InCallService} source.
3357      *
3358      * @param extras The extras.
3359      */
3360     public void putConnectionServiceExtras(Bundle extras) {
3361         putExtras(SOURCE_CONNECTION_SERVICE, extras, null);
3362     }
3363 
3364     /**
3365      * Adds extras to the extras bundle associated with this {@link Call}, as made by a
3366      * {@link android.telecom.InCallService}.
3367      * @param extras the extras.
3368      * @param requestingPackageName the package name of the {@link android.telecom.InCallService}
3369      *                              which requested the extras changed; required so that when we
3370      *                              have {@link InCallController} notify other
3371      *                              {@link android.telecom.InCallService}s we don't notify the
3372      *                              originator of their own change.
3373      */
3374     public void putInCallServiceExtras(Bundle extras, String requestingPackageName) {
3375         putExtras(SOURCE_INCALL_SERVICE, extras, requestingPackageName);
3376     }
3377 
3378     /**
3379      * Adds extras to the extras bundle associated with this {@link Call}.
3380      *
3381      * Note: this method needs to know the source of the extras change (see
3382      * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}).  Extras changes which
3383      * originate from a connection service will only be notified to incall services.  Changes
3384      * originating from the InCallServices will notify the connection service of the
3385      * change, as well as other InCallServices other than the originator.
3386      *
3387      * @param source The source of the extras addition.
3388      * @param extras The extras.
3389      * @param requestingPackageName The package name which requested the extras change.  For
3390      *                              {@link #SOURCE_INCALL_SERVICE} will be populated with the
3391      *                              package name of the ICS that requested the change.
3392      */
3393     private void putExtras(int source, Bundle extras, String requestingPackageName) {
3394         if (extras == null) {
3395             return;
3396         }
3397         if (mExtras == null) {
3398             mExtras = new Bundle();
3399         }
3400         mExtras.putAll(extras);
3401 
3402         for (Listener l : mListeners) {
3403             l.onExtrasChanged(this, source, extras, requestingPackageName);
3404         }
3405 
3406         // If mExtra shows that the call using Volte, record it with mWasVolte
3407         if (mExtras.containsKey(TelecomManager.EXTRA_CALL_NETWORK_TYPE) &&
3408             mExtras.get(TelecomManager.EXTRA_CALL_NETWORK_TYPE)
3409                     .equals(TelephonyManager.NETWORK_TYPE_LTE)) {
3410             mWasVolte = true;
3411         }
3412 
3413         if (extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
3414             setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID));
3415         }
3416 
3417         if (extras.containsKey(Connection.EXTRA_CALLER_NUMBER_VERIFICATION_STATUS)
3418                 && source == SOURCE_CONNECTION_SERVICE) {
3419             int callerNumberVerificationStatus =
3420                     extras.getInt(Connection.EXTRA_CALLER_NUMBER_VERIFICATION_STATUS);
3421             if (mCallerNumberVerificationStatus != callerNumberVerificationStatus) {
3422                 Log.addEvent(this, LogUtils.Events.VERSTAT_CHANGED, callerNumberVerificationStatus);
3423                 setCallerNumberVerificationStatus(callerNumberVerificationStatus);
3424             }
3425         }
3426 
3427         if (extras.containsKey(Connection.EXTRA_ANSWERING_DROPS_FG_CALL)) {
3428             CharSequence appName =
3429                     extras.getCharSequence(Connection.EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME);
3430             Log.addEvent(this, LogUtils.Events.ANSWER_DROPS_FG,
3431                     "Answering will drop FG call from %s", appName);
3432         }
3433 
3434         // The remote connection service API can track the phone account which was originally
3435         // requested to create a connection via the remote connection service API; we store that so
3436         // we have some visibility into how a call was actually placed.
3437         if (mExtras.containsKey(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE)) {
3438             setRemotePhoneAccountHandle(extras.getParcelable(
3439                     Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE));
3440         }
3441 
3442         if (mExtras.containsKey(TelecomManager.EXTRA_DO_NOT_LOG_CALL)) {
3443             if (source != SOURCE_CONNECTION_SERVICE || !mIsModifyStatePermissionGranted) {
3444                 mExtras.remove(TelecomManager.EXTRA_DO_NOT_LOG_CALL);
3445             }
3446         }
3447 
3448         // If the change originated from an InCallService, notify the connection service.
3449         if (source == SOURCE_INCALL_SERVICE) {
3450             Log.addEvent(this, LogUtils.Events.ICS_EXTRAS_CHANGED);
3451             if (mTransactionalService != null) {
3452                 Log.i(this, "putExtras: called on TransactionalService. doing nothing");
3453             } else if (mConnectionService != null) {
3454                 mConnectionService.onExtrasChanged(this, mExtras);
3455             } else {
3456                 Log.w(this, "putExtras failed due to null CS callId=%s", getId());
3457             }
3458         }
3459     }
3460 
3461     private boolean isModifyPhoneStatePermissionGranted(PhoneAccountHandle phoneAccountHandle) {
3462         if (phoneAccountHandle == null) {
3463             return false;
3464         }
3465         String packageName = phoneAccountHandle.getComponentName().getPackageName();
3466         return PackageManager.PERMISSION_GRANTED == mContext.getPackageManager().checkPermission(
3467                 android.Manifest.permission.MODIFY_PHONE_STATE, packageName);
3468     }
3469 
3470     /**
3471      * Removes extras from the extras bundle associated with this {@link Call}.
3472      *
3473      * Note: this method needs to know the source of the extras change (see
3474      * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}).  Extras changes which
3475      * originate from a connection service will only be notified to incall services.  Likewise,
3476      * changes originating from the incall services will only notify the connection service of the
3477      * change.
3478      *
3479      * @param source The source of the extras removal.
3480      * @param keys The extra keys to remove.
3481      */
3482     void removeExtras(int source, List<String> keys) {
3483         if (mExtras == null) {
3484             return;
3485         }
3486         for (String key : keys) {
3487             mExtras.remove(key);
3488         }
3489 
3490         for (Listener l : mListeners) {
3491             l.onExtrasRemoved(this, source, keys);
3492         }
3493 
3494         // If the change originated from an InCallService, notify the connection service.
3495         if (source == SOURCE_INCALL_SERVICE) {
3496             if (mTransactionalService != null) {
3497                 Log.i(this, "removeExtras: called on TransactionalService. doing nothing");
3498             } else if (mConnectionService != null) {
3499                 mConnectionService.onExtrasChanged(this, mExtras);
3500             } else {
3501                 Log.e(this, new NullPointerException(),
3502                         "removeExtras failed due to null CS callId=%s", getId());
3503             }
3504         }
3505     }
3506 
3507     @VisibleForTesting
3508     public Bundle getIntentExtras() {
3509         return mIntentExtras;
3510     }
3511 
3512     void setIntentExtras(Bundle extras) {
3513         mIntentExtras = extras;
3514     }
3515 
3516     public Intent getOriginalCallIntent() {
3517         return mOriginalCallIntent;
3518     }
3519 
3520     public void setOriginalCallIntent(Intent intent) {
3521         mOriginalCallIntent = intent;
3522     }
3523 
3524     /**
3525      * @return the uri of the contact associated with this call.
3526      */
3527     @VisibleForTesting
3528     public Uri getContactUri() {
3529         if (mCallerInfo == null || !mCallerInfo.contactExists) {
3530             return getHandle();
3531         }
3532         return Contacts.getLookupUri(mCallerInfo.getContactId(), mCallerInfo.lookupKey);
3533     }
3534 
3535     Uri getRingtone() {
3536         return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
3537     }
3538 
3539     void onPostDialWait(String remaining) {
3540         for (Listener l : mListeners) {
3541             l.onPostDialWait(this, remaining);
3542         }
3543     }
3544 
3545     void onPostDialChar(char nextChar) {
3546         for (Listener l : mListeners) {
3547             l.onPostDialChar(this, nextChar);
3548         }
3549     }
3550 
3551     void postDialContinue(boolean proceed) {
3552         if (mTransactionalService != null) {
3553             Log.i(this, "postDialContinue: called on TransactionalService. doing nothing");
3554         } else if (mConnectionService != null) {
3555             mConnectionService.onPostDialContinue(this, proceed);
3556         } else {
3557             Log.e(this, new NullPointerException(),
3558                     "postDialContinue failed due to null CS callId=%s", getId());
3559         }
3560     }
3561 
3562     void conferenceWith(Call otherCall) {
3563         if (mTransactionalService != null) {
3564             Log.i(this, "conferenceWith: called on TransactionalService. doing nothing");
3565         } else if (mConnectionService == null) {
3566             Log.w(this, "conference requested on a call without a connection service.");
3567         } else {
3568             Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH, otherCall);
3569             mConnectionService.conference(this, otherCall);
3570         }
3571     }
3572 
3573     void splitFromConference() {
3574         if (mTransactionalService != null) {
3575             Log.i(this, "splitFromConference: called on TransactionalService. doing nothing");
3576         } else if (mConnectionService == null) {
3577             Log.w(this, "splitting from conference call without a connection service");
3578         } else {
3579             Log.addEvent(this, LogUtils.Events.SPLIT_FROM_CONFERENCE);
3580             mConnectionService.splitFromConference(this);
3581         }
3582     }
3583 
3584     @VisibleForTesting
3585     public void mergeConference() {
3586         if (mTransactionalService != null) {
3587             Log.i(this, "mergeConference: called on TransactionalService. doing nothing");
3588         } else if (mConnectionService == null) {
3589             Log.w(this, "merging conference calls without a connection service.");
3590         } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
3591             Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH);
3592             mConnectionService.mergeConference(this);
3593             mWasConferencePreviouslyMerged = true;
3594         }
3595     }
3596 
3597     @VisibleForTesting
3598     public void swapConference() {
3599         if (mTransactionalService != null) {
3600             Log.i(this, "swapConference: called on TransactionalService. doing nothing");
3601         } else if (mConnectionService == null) {
3602             Log.w(this, "swapping conference calls without a connection service.");
3603         } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
3604             Log.addEvent(this, LogUtils.Events.SWAP);
3605             mConnectionService.swapConference(this);
3606             switch (mChildCalls.size()) {
3607                 case 1:
3608                     mConferenceLevelActiveCall = mChildCalls.get(0);
3609                     break;
3610                 case 2:
3611                     // swap
3612                     mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ?
3613                             mChildCalls.get(1) : mChildCalls.get(0);
3614                     break;
3615                 default:
3616                     // For anything else 0, or 3+, set it to null since it is impossible to tell.
3617                     mConferenceLevelActiveCall = null;
3618                     break;
3619             }
3620             for (Listener l : mListeners) {
3621                 l.onCdmaConferenceSwap(this);
3622             }
3623         }
3624     }
3625 
3626     public void addConferenceParticipants(List<Uri> participants) {
3627         if (mTransactionalService != null) {
3628             Log.i(this, "addConferenceParticipants: called on TransactionalService. doing nothing");
3629         } else if (mConnectionService == null) {
3630             Log.w(this, "adding conference participants without a connection service.");
3631         } else if (can(Connection.CAPABILITY_ADD_PARTICIPANT)) {
3632             Log.addEvent(this, LogUtils.Events.ADD_PARTICIPANT);
3633             mConnectionService.addConferenceParticipants(this, participants);
3634         }
3635     }
3636 
3637     /**
3638      * Initiates a request to the connection service to pull this call.
3639      * <p>
3640      * This method can only be used for calls that have the
3641      * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL} capability and
3642      * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
3643      * <p>
3644      * An external call is a representation of a call which is taking place on another device
3645      * associated with a PhoneAccount on this device.  Issuing a request to pull the external call
3646      * tells the {@link android.telecom.ConnectionService} that it should move the call from the
3647      * other device to this one.  An example of this is the IMS multi-endpoint functionality.  A
3648      * user may have two phones with the same phone number.  If the user is engaged in an active
3649      * call on their first device, the network will inform the second device of that ongoing call in
3650      * the form of an external call.  The user may wish to continue their conversation on the second
3651      * device, so will issue a request to pull the call to the second device.
3652      * <p>
3653      * Requests to pull a call which is not external, or a call which is not pullable are ignored.
3654      * If there is an ongoing emergency call, pull requests are also ignored.
3655      */
3656     public void pullExternalCall() {
3657         if (mTransactionalService != null) {
3658             Log.i(this, "transfer: called on TransactionalService. doing nothing");
3659             return;
3660         }
3661 
3662         if (mConnectionService == null) {
3663             Log.w(this, "pulling a call without a connection service.");
3664         }
3665 
3666         if (!hasProperty(Connection.PROPERTY_IS_EXTERNAL_CALL)) {
3667             Log.w(this, "pullExternalCall - call %s is not an external call.", mId);
3668             return;
3669         }
3670 
3671         if (!can(Connection.CAPABILITY_CAN_PULL_CALL)) {
3672             Log.w(this, "pullExternalCall - call %s is external but cannot be pulled.", mId);
3673             return;
3674         }
3675 
3676         if (mCallsManager.isInEmergencyCall()) {
3677             Log.w(this, "pullExternalCall = pullExternalCall - call %s is external but can not be"
3678                     + " pulled while an emergency call is in progress.", mId);
3679             mToastFactory.makeText(mContext, R.string.toast_emergency_can_not_pull_call,
3680                     Toast.LENGTH_LONG);
3681             return;
3682         }
3683 
3684         Log.addEvent(this, LogUtils.Events.REQUEST_PULL);
3685         mConnectionService.pullExternalCall(this);
3686     }
3687 
3688     /**
3689      * Sends a call event to the {@link ConnectionService} for this call.
3690      *
3691      * @param event The call event.
3692      * @param extras Associated extras.
3693      */
3694     public void sendCallEvent(String event, Bundle extras) {
3695         if (mConnectionService != null || mTransactionalService != null) {
3696             // Relay bluetooth call quality reports to the call diagnostic service.
3697             if (BluetoothCallQualityReport.EVENT_BLUETOOTH_CALL_QUALITY_REPORT.equals(event)
3698                     && extras.containsKey(
3699                     BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT)) {
3700                 notifyBluetoothCallQualityReport(extras.getParcelable(
3701                         BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT
3702                 ));
3703             }
3704             Log.addEvent(this, LogUtils.Events.CALL_EVENT, event);
3705             sendEventToService(this, event, extras);
3706         } else {
3707             if (mFlags.cacheCallEvents()) {
3708                 Log.i(this, "sendCallEvent: caching call event for callId=%s, event=%s",
3709                         getId(), event);
3710                 cacheServiceCallback(new CachedCallEventQueue(event, extras));
3711             } else {
3712                 Log.e(this, new NullPointerException(),
3713                         "sendCallEvent failed due to null CS callId=%s", getId());
3714             }
3715         }
3716     }
3717 
3718     /**
3719      *  This method should only be called from sendCallEvent(String, Bundle).
3720      */
3721     private void sendEventToService(Call call, String event, Bundle extras) {
3722         if (mConnectionService != null) {
3723             mConnectionService.sendCallEvent(call, event, extras);
3724         } else if (mTransactionalService != null) {
3725             mTransactionalService.sendCallEvent(call, event, extras);
3726         }
3727     }
3728 
3729     /**
3730      * Notifies listeners when a bluetooth quality report is received.
3731      * @param report The bluetooth quality report.
3732      */
3733     void notifyBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport report) {
3734         Log.addEvent(this, LogUtils.Events.BT_QUALITY_REPORT, "choppy=" + report.isChoppyVoice());
3735         for (Listener l : mListeners) {
3736             l.onBluetoothCallQualityReport(this, report);
3737         }
3738     }
3739 
3740     /**
3741      * Initiates a handover of this Call to the {@link ConnectionService} identified
3742      * by destAcct.
3743      * @param destAcct ConnectionService to which the call should be handed over.
3744      * @param videoState The video state desired after the handover.
3745      * @param extras Extra information to be passed to ConnectionService
3746      */
3747     public void handoverTo(PhoneAccountHandle destAcct, int videoState, Bundle extras) {
3748         requestHandover(destAcct, videoState, extras, false);
3749     }
3750 
3751     /**
3752      * Sets this {@link Call} to has the specified {@code parentCall}.  Also sets the parent to
3753      * have this call as a child.
3754      * @param parentCall
3755      */
3756     void setParentAndChildCall(Call parentCall) {
3757         boolean isParentChanging = (mParentCall != parentCall);
3758         setParentCall(parentCall);
3759         setChildOf(parentCall);
3760         if (isParentChanging) {
3761             notifyParentChanged(parentCall);
3762         }
3763     }
3764 
3765     /**
3766      * Notifies listeners when the parent call changes.
3767      * Used by {@link #setParentAndChildCall(Call)}, and in {@link CallsManager}.
3768      * @param parentCall The new parent call for this call.
3769      */
3770     void notifyParentChanged(Call parentCall) {
3771         Log.addEvent(this, LogUtils.Events.SET_PARENT, parentCall);
3772         for (Listener l : mListeners) {
3773             l.onParentChanged(this);
3774         }
3775     }
3776 
3777     /**
3778      * Unlike {@link #setParentAndChildCall(Call)}, only sets the parent call but does NOT set
3779      * the child.
3780      * TODO: This is only required when adding existing connections as a workaround so that we
3781      * can avoid sending the "onParentChanged" callback until later.
3782      * @param parentCall The new parent call.
3783      */
3784     void setParentCall(Call parentCall) {
3785         if (parentCall == this) {
3786             Log.e(this, new Exception(), "setting the parent to self");
3787             return;
3788         }
3789         if (parentCall == mParentCall) {
3790             // nothing to do
3791             return;
3792         }
3793         if (mParentCall != null) {
3794             mParentCall.removeChildCall(this);
3795         }
3796         mParentCall = parentCall;
3797     }
3798 
3799     /**
3800      * To be called after {@link #setParentCall(Call)} to complete setting the parent by adding
3801      * this call as a child of another call.
3802      * <p>
3803      * Note: if using this method alone, the caller must call {@link #notifyParentChanged(Call)} to
3804      * ensure the InCall UI is updated with the change in parent.
3805      * @param parentCall The new parent for this call.
3806      */
3807     public void setChildOf(Call parentCall) {
3808         if (parentCall != null && !parentCall.getChildCalls().contains(this)) {
3809             parentCall.addChildCall(this);
3810         }
3811     }
3812 
3813     void setConferenceableCalls(List<Call> conferenceableCalls) {
3814         mConferenceableCalls.clear();
3815         mConferenceableCalls.addAll(conferenceableCalls);
3816         String confCallIds = "";
3817         if (!conferenceableCalls.isEmpty()) {
3818             confCallIds = conferenceableCalls.stream()
3819                     .map(c -> c.getId())
3820                     .collect(Collectors.joining(","));
3821         }
3822         Log.addEvent(this, LogUtils.Events.CONF_CALLS_CHANGED, confCallIds);
3823 
3824         for (Listener l : mListeners) {
3825             l.onConferenceableCallsChanged(this);
3826         }
3827     }
3828 
3829     @VisibleForTesting
3830     public List<Call> getConferenceableCalls() {
3831         return mConferenceableCalls;
3832     }
3833 
3834     @VisibleForTesting
3835     public boolean can(int capability) {
3836         return (getConnectionCapabilities() & capability) == capability;
3837     }
3838 
3839     @VisibleForTesting
3840     public boolean hasProperty(int property) {
3841         return (mConnectionProperties & property) == property;
3842     }
3843 
3844     private void addChildCall(Call call) {
3845         if (!mChildCalls.contains(call)) {
3846             mHadChildren = true;
3847             // Set the pseudo-active call to the latest child added to the conference.
3848             // See definition of mConferenceLevelActiveCall for more detail.
3849             mConferenceLevelActiveCall = call;
3850             mChildCalls.add(call);
3851 
3852             // When adding a child, we will potentially adjust the various times from the calls
3853             // based on the children being added.  This ensures the parent of the conference has a
3854             // connect time reflective of all the children added.
3855             maybeAdjustConnectTime(call);
3856 
3857             Log.addEvent(this, LogUtils.Events.ADD_CHILD, call);
3858 
3859             for (Listener l : mListeners) {
3860                 l.onChildrenChanged(this);
3861             }
3862         }
3863     }
3864 
3865     /**
3866      * Potentially adjust the connect and creation time of this call based on another one.
3867      * Ensures that if the other call has an earlier connect time that we adjust the connect time of
3868      * this call to match.
3869      * <p>
3870      * This is important for conference calls; as we add children to the conference we need to
3871      * ensure that earlier connect time is reflected on the conference.  In the past this
3872      * was just done in {@link ParcelableCallUtils} when parceling the calls to the UI, but that
3873      * approach would not reflect the right time on the parent as children disconnect.
3874      *
3875      * @param call the call to potentially use to adjust connect time.
3876      */
3877     private void maybeAdjustConnectTime(@NonNull Call call) {
3878         long childConnectTimeMillis = call.getConnectTimeMillis();
3879         long currentConnectTimeMillis = getConnectTimeMillis();
3880         // Conference calls typically have a 0 connect time, so we will replace the current connect
3881         // time if its zero also.
3882         if (childConnectTimeMillis != 0
3883                 && (currentConnectTimeMillis == 0
3884                 || childConnectTimeMillis < getConnectTimeMillis())) {
3885             setConnectTimeMillis(childConnectTimeMillis);
3886         }
3887     }
3888 
3889     private void removeChildCall(Call call) {
3890         if (mChildCalls.remove(call)) {
3891             Log.addEvent(this, LogUtils.Events.REMOVE_CHILD, call);
3892             for (Listener l : mListeners) {
3893                 l.onChildrenChanged(this);
3894             }
3895         }
3896     }
3897 
3898     /**
3899      * Return whether the user can respond to this {@code Call} via an SMS message.
3900      *
3901      * @return true if the "Respond via SMS" feature should be enabled
3902      * for this incoming call.
3903      *
3904      * The general rule is that we *do* allow "Respond via SMS" except for
3905      * the few (relatively rare) cases where we know for sure it won't
3906      * work, namely:
3907      *   - a bogus or blank incoming number
3908      *   - a call from a SIP address
3909      *   - a "call presentation" that doesn't allow the number to be revealed
3910      *
3911      * In all other cases, we allow the user to respond via SMS.
3912      *
3913      * Note that this behavior isn't perfect; for example we have no way
3914      * to detect whether the incoming call is from a landline (with most
3915      * networks at least), so we still enable this feature even though
3916      * SMSes to that number will silently fail.
3917      */
3918     public boolean isRespondViaSmsCapable() {
3919         if (mContext.getResources().getBoolean(R.bool.skip_loading_canned_text_response)) {
3920             Log.d(this, "maybeLoadCannedSmsResponses: skip loading due to setting");
3921             return false;
3922         }
3923 
3924         if (mState != CallState.RINGING) {
3925             return false;
3926         }
3927 
3928         if (getHandle() == null) {
3929             // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
3930             // other words, the user should not be able to see the incoming phone number.
3931             return false;
3932         }
3933 
3934         if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) {
3935             // The incoming number is actually a URI (i.e. a SIP address),
3936             // not a regular PSTN phone number, and we can't send SMSes to
3937             // SIP addresses.
3938             // (TODO: That might still be possible eventually, though. Is
3939             // there some SIP-specific equivalent to sending a text message?)
3940             return false;
3941         }
3942 
3943         // Is there a valid SMS application on the phone?
3944         try {
3945             if (mContext.getSystemService(TelephonyManager.class)
3946                     .getAndUpdateDefaultRespondViaMessageApplication() == null) {
3947                 return false;
3948             }
3949         } catch (UnsupportedOperationException uoe) {
3950             return false;
3951         }
3952 
3953         // TODO: with some carriers (in certain countries) you *can* actually
3954         // tell whether a given number is a mobile phone or not. So in that
3955         // case we could potentially return false here if the incoming call is
3956         // from a land line.
3957 
3958         // If none of the above special cases apply, it's OK to enable the
3959         // "Respond via SMS" feature.
3960         return true;
3961     }
3962 
3963     List<String> getCannedSmsResponses() {
3964         return mCannedSmsResponses;
3965     }
3966 
3967     /**
3968      * We need to make sure that before we move a call to the disconnected state, it no
3969      * longer has any parent/child relationships.  We want to do this to ensure that the InCall
3970      * Service always has the right data in the right order.  We also want to do it in telecom so
3971      * that the insurance policy lives in the framework side of things.
3972      */
3973     private void fixParentAfterDisconnect() {
3974         setParentAndChildCall(null);
3975     }
3976 
3977     /**
3978      * @return True if the call is ringing, else logs the action name.
3979      */
3980     private boolean isRinging(String actionName) {
3981         if (mState == CallState.RINGING || mState == CallState.ANSWERED) {
3982             return true;
3983         }
3984 
3985         Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
3986         return false;
3987     }
3988 
3989     /**
3990      * @return True if the call is answered, else logs the action name.
3991      */
3992     private boolean isAnswered(String actionName) {
3993         if (mState == CallState.ANSWERED) {
3994             return true;
3995         }
3996 
3997         Log.i(this, "Request to %s a non-answered call %s", actionName, this);
3998         return false;
3999     }
4000 
4001     @SuppressWarnings("rawtypes")
4002     private void decrementAssociatedCallCount(ServiceBinder binder) {
4003         if (binder != null) {
4004             binder.decrementAssociatedCallCount();
4005         }
4006     }
4007 
4008     /**
4009      * Looks up contact information based on the current handle.
4010      */
4011     private void startCallerInfoLookup() {
4012         mCallerInfo = null;
4013         mCallsManager.getCallerInfoLookupHelper().startLookup(mHandle, mCallerInfoQueryListener);
4014     }
4015 
4016     /**
4017      * Saves the specified caller info if the specified token matches that of the last query
4018      * that was made.
4019      *
4020      * @param callerInfo The new caller information to set.
4021      */
4022     private void setCallerInfo(Uri handle, CallerInfo callerInfo) {
4023         if (callerInfo == null) {
4024             Log.i(this, "CallerInfo lookup returned null, skipping update");
4025             return;
4026         }
4027 
4028         if ((handle != null) && !handle.equals(mHandle)) {
4029             Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring.");
4030             return;
4031         }
4032 
4033         String newName = callerInfo.getName();
4034         boolean contactNameChanged = mCallerInfo == null ||
4035                 !Objects.equals(mCallerInfo.getName(), newName);
4036 
4037         mCallerInfo = callerInfo;
4038         Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
4039 
4040         if (mCallerInfo.getContactDisplayPhotoUri() == null || mCallerInfo.cachedPhotoIcon != null
4041             || mCallerInfo.cachedPhoto != null || contactNameChanged) {
4042             for (Listener l : mListeners) {
4043                 l.onCallerInfoChanged(this);
4044             }
4045         }
4046     }
4047 
4048     public CallerInfo getCallerInfo() {
4049         return mCallerInfo;
4050     }
4051 
4052     private void maybeLoadCannedSmsResponses() {
4053         if (mCallDirection == CALL_DIRECTION_INCOMING
4054                 && isRespondViaSmsCapable()
4055                 && !mCannedSmsResponsesLoadingStarted) {
4056             Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
4057             mCannedSmsResponsesLoadingStarted = true;
4058             mCallsManager.getRespondViaSmsManager().loadCannedTextMessages(
4059                     new CallsManager.Response<Void, List<String>>() {
4060                         @Override
4061                         public void onResult(Void request, List<String>... result) {
4062                             if (result.length > 0) {
4063                                 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
4064                                 mCannedSmsResponses = result[0];
4065                                 for (Listener l : mListeners) {
4066                                     l.onCannedSmsResponsesLoaded(Call.this);
4067                                 }
4068                             }
4069                         }
4070 
4071                         @Override
4072                         public void onError(Void request, int code, String msg) {
4073                             Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
4074                                     msg);
4075                         }
4076                     },
4077                     mContext
4078             );
4079         } else {
4080             Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
4081         }
4082     }
4083 
4084     /**
4085      * Sets speakerphone option on when call begins.
4086      */
4087     public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
4088         mSpeakerphoneOn = startWithSpeakerphone;
4089     }
4090 
4091     /**
4092      * Returns speakerphone option.
4093      *
4094      * @return Whether or not speakerphone should be set automatically when call begins.
4095      */
4096     public boolean getStartWithSpeakerphoneOn() {
4097         return mSpeakerphoneOn;
4098     }
4099 
4100     public void setRequestedToStartWithRtt() {
4101         mDidRequestToStartWithRtt = true;
4102     }
4103 
4104     public void stopRtt() {
4105         if (mTransactionalService != null) {
4106             Log.i(this, "stopRtt: called on TransactionalService. doing nothing");
4107         } else if (mConnectionService != null) {
4108             Log.addEvent(this, LogUtils.Events.REQUEST_RTT, "stop");
4109             mConnectionService.stopRtt(this);
4110         } else {
4111             // If this gets called by the in-call app before the connection service is set, we'll
4112             // just ignore it since it's really not supposed to happen.
4113             Log.w(this, "stopRtt() called before connection service is set.");
4114         }
4115     }
4116 
4117     public void sendRttRequest() {
4118         if (mTransactionalService != null) {
4119             Log.i(this, "sendRttRequest: called on TransactionalService. doing nothing");
4120             return;
4121         }
4122         Log.addEvent(this, LogUtils.Events.REQUEST_RTT, "start");
4123         createRttStreams();
4124         mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
4125     }
4126 
4127     private boolean areRttStreamsInitialized() {
4128         return mInCallToConnectionServiceStreams != null
4129                 && mConnectionServiceToInCallStreams != null;
4130     }
4131 
4132     public void createRttStreams() {
4133         if (!areRttStreamsInitialized()) {
4134             Log.i(this, "Initializing RTT streams");
4135             try {
4136                 mInCallToConnectionServiceStreams = ParcelFileDescriptor.createReliablePipe();
4137                 mConnectionServiceToInCallStreams = ParcelFileDescriptor.createReliablePipe();
4138             } catch (IOException e) {
4139                 Log.e(this, e, "Failed to create pipes for RTT call.");
4140             }
4141         }
4142     }
4143 
4144     public void onRttConnectionFailure(int reason) {
4145         Log.i(this, "Got RTT initiation failure with reason %d", reason);
4146         Log.addEvent(this, LogUtils.Events.ON_RTT_FAILED, "reason="  + reason);
4147         for (Listener l : mListeners) {
4148             l.onRttInitiationFailure(this, reason);
4149         }
4150     }
4151 
4152     public void onRemoteRttRequest() {
4153         Log.addEvent(this, LogUtils.Events.ON_RTT_REQUEST);
4154         if (isRttCall()) {
4155             Log.w(this, "Remote RTT request on a call that's already RTT");
4156             return;
4157         }
4158 
4159         mPendingRttRequestId = mCallsManager.getNextRttRequestId();
4160         for (Listener l : mListeners) {
4161             l.onRemoteRttRequest(this, mPendingRttRequestId);
4162         }
4163     }
4164 
4165     public void handleRttRequestResponse(int id, boolean accept) {
4166         if (mPendingRttRequestId == INVALID_RTT_REQUEST_ID) {
4167             Log.w(this, "Response received to a nonexistent RTT request: %d", id);
4168             return;
4169         }
4170         if (id != mPendingRttRequestId) {
4171             Log.w(this, "Response ID %d does not match expected %d", id, mPendingRttRequestId);
4172             return;
4173         }
4174         if (mTransactionalService != null) {
4175             Log.i(this, "handleRttRequestResponse: called on TransactionalService. doing nothing");
4176             return;
4177         }
4178         Log.addEvent(this, LogUtils.Events.RESPOND_TO_RTT_REQUEST, "id=" + id + ", accept="
4179                 + accept);
4180         if (accept) {
4181             createRttStreams();
4182             Log.i(this, "RTT request %d accepted.", id);
4183             mConnectionService.respondToRttRequest(
4184                     this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
4185         } else {
4186             Log.i(this, "RTT request %d rejected.", id);
4187             mConnectionService.respondToRttRequest(this, null, null);
4188         }
4189     }
4190 
4191     public boolean isRttCall() {
4192         return (mConnectionProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
4193     }
4194 
4195     public boolean wasEverRttCall() {
4196         return mWasEverRtt;
4197     }
4198 
4199     public ParcelFileDescriptor getCsToInCallRttPipeForCs() {
4200         return mConnectionServiceToInCallStreams == null ? null
4201                 : mConnectionServiceToInCallStreams[RTT_PIPE_WRITE_SIDE_INDEX];
4202     }
4203 
4204     public ParcelFileDescriptor getInCallToCsRttPipeForCs() {
4205         return mInCallToConnectionServiceStreams == null ? null
4206                 : mInCallToConnectionServiceStreams[RTT_PIPE_READ_SIDE_INDEX];
4207     }
4208 
4209     public ParcelFileDescriptor getCsToInCallRttPipeForInCall() {
4210         return mConnectionServiceToInCallStreams == null ? null
4211                 : mConnectionServiceToInCallStreams[RTT_PIPE_READ_SIDE_INDEX];
4212     }
4213 
4214     public ParcelFileDescriptor getInCallToCsRttPipeForInCall() {
4215         return mInCallToConnectionServiceStreams == null ? null
4216                 : mInCallToConnectionServiceStreams[RTT_PIPE_WRITE_SIDE_INDEX];
4217     }
4218 
4219     public int getRttMode() {
4220         return mRttMode;
4221     }
4222 
4223     /**
4224      * Sets a video call provider for the call.
4225      */
4226     public void setVideoProvider(IVideoProvider videoProvider) {
4227         Log.v(this, "setVideoProvider");
4228 
4229         if (mVideoProviderProxy != null) {
4230             mVideoProviderProxy.clearVideoCallback();
4231             mVideoProviderProxy = null;
4232         }
4233 
4234         if (videoProvider != null ) {
4235             try {
4236                 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this,
4237                         mCallsManager);
4238             } catch (RemoteException ignored) {
4239                 // Ignore RemoteException.
4240             }
4241         }
4242 
4243         for (Listener l : mListeners) {
4244             l.onVideoCallProviderChanged(Call.this);
4245         }
4246     }
4247 
4248     /**
4249      * @return The {@link Connection.VideoProvider} binder.
4250      */
4251     public IVideoProvider getVideoProvider() {
4252         if (mVideoProviderProxy == null) {
4253             return null;
4254         }
4255 
4256         return mVideoProviderProxy.getInterface();
4257     }
4258 
4259     /**
4260      * @return The {@link VideoProviderProxy} for this call.
4261      */
4262     public VideoProviderProxy getVideoProviderProxy() {
4263         return mVideoProviderProxy;
4264     }
4265 
4266     /**
4267      * The current video state for the call.
4268      * See {@link VideoProfile} for a list of valid video states.
4269      */
4270     public int getVideoState() {
4271         return mVideoState;
4272     }
4273 
4274     /**
4275      * Returns the video states which were applicable over the duration of a call.
4276      * See {@link VideoProfile} for a list of valid video states.
4277      *
4278      * @return The video states applicable over the duration of the call.
4279      */
4280     public int getVideoStateHistory() {
4281         return mVideoStateHistory;
4282     }
4283 
4284     /**
4285      * Determines the current video state for the call.
4286      * For an outgoing call determines the desired video state for the call.
4287      * Valid values: see {@link VideoProfile}
4288      *
4289      * @param videoState The video state for the call.
4290      */
4291     public void setVideoState(int videoState) {
4292         // If the phone account associated with this call does not support video calling, then we
4293         // will automatically set the video state to audio-only.
4294         if (!isVideoCallingSupportedByPhoneAccount()) {
4295             Log.d(this, "setVideoState: videoState=%s defaulted to audio (video not supported)",
4296                     VideoProfile.videoStateToString(videoState));
4297             videoState = VideoProfile.STATE_AUDIO_ONLY;
4298         }
4299 
4300         // TODO:: b/338280297. If a transactional call does not have the
4301         //   CallAttributes.SUPPORTS_VIDEO_CALLING capability, the videoState should be set to audio
4302 
4303         // Track Video State history during the duration of the call.
4304         // Only update the history when the call is active or disconnected. This ensures we do
4305         // not include the video state history when:
4306         // - Call is incoming (but not answered).
4307         // - Call it outgoing (but not answered).
4308         // We include the video state when disconnected to ensure that rejected calls reflect the
4309         // appropriate video state.
4310         // For all other times we add to the video state history, see #setState.
4311         if (isActive() || getState() == CallState.DISCONNECTED) {
4312             mVideoStateHistory = mVideoStateHistory | videoState;
4313         }
4314 
4315         int previousVideoState = mVideoState;
4316         mVideoState = videoState;
4317         if (mVideoState != previousVideoState) {
4318             if (!mIsTransactionalCall) {
4319                 Log.addEvent(this, LogUtils.Events.VIDEO_STATE_CHANGED,
4320                         VideoProfile.videoStateToString(videoState));
4321             }
4322             for (Listener l : mListeners) {
4323                 l.onVideoStateChanged(this, previousVideoState, mVideoState);
4324             }
4325         }
4326 
4327         if (mFlags.transactionalVideoState() && mIsTransactionalCall) {
4328             int transactionalVS = VideoProfileStateToTransactionalVideoState(mVideoState);
4329             if (mTransactionalService != null) {
4330                 Log.addEvent(this, LogUtils.Events.VIDEO_STATE_CHANGED,
4331                         TransactionalVideoStateToString(transactionalVS));
4332                 mTransactionalService.onVideoStateChanged(this, transactionalVS);
4333             } else {
4334                 cacheServiceCallback(new CachedVideoStateChange(transactionalVS));
4335             }
4336         }
4337 
4338         if (VideoProfile.isVideo(videoState)) {
4339             mHasVideoCall = true;
4340             mAnalytics.setCallIsVideo(true);
4341         }
4342     }
4343 
4344     public boolean getIsVoipAudioMode() {
4345         return mIsVoipAudioMode;
4346     }
4347 
4348     public void setIsVoipAudioMode(boolean audioModeIsVoip) {
4349         if (isSelfManaged() && !audioModeIsVoip) {
4350             Log.i(this,
4351                     "setIsVoipAudioMode: ignoring request to set self-managed audio to "
4352                             + "non-voip mode");
4353             return;
4354         }
4355         if (mIsVoipAudioMode != audioModeIsVoip) {
4356             Log.addEvent(this, LogUtils.Events.SET_VOIP_MODE, audioModeIsVoip ? "Y" : "N");
4357         }
4358         mIsVoipAudioMode = audioModeIsVoip;
4359         for (Listener l : mListeners) {
4360             l.onIsVoipAudioModeChanged(this);
4361         }
4362     }
4363 
4364     public StatusHints getStatusHints() {
4365         return mStatusHints;
4366     }
4367 
4368     public void setStatusHints(StatusHints statusHints) {
4369         mStatusHints = statusHints;
4370         for (Listener l : mListeners) {
4371             l.onStatusHintsChanged(this);
4372         }
4373     }
4374 
4375     public boolean isUnknown() {
4376         return mCallDirection == CALL_DIRECTION_UNKNOWN;
4377     }
4378 
4379     public boolean isOutgoing() {
4380         return mCallDirection == CALL_DIRECTION_OUTGOING;
4381     }
4382 
4383     /**
4384      * Determines if this call is in a disconnecting state.
4385      *
4386      * @return {@code true} if this call is locally disconnecting.
4387      */
4388     public boolean isLocallyDisconnecting() {
4389         return mIsLocallyDisconnecting;
4390     }
4391 
4392     /**
4393      * Sets whether this call is in a disconnecting state.
4394      *
4395      * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting.
4396      */
4397     private void setLocallyDisconnecting(boolean isLocallyDisconnecting) {
4398         mIsLocallyDisconnecting = isLocallyDisconnecting;
4399     }
4400 
4401     /**
4402      * It's possible that the target phone account isn't set when a user hasn't selected a
4403      * default sim to place a call. Instead of using the user from the target phone account to
4404      * associate the user with a call, we'll use mAssociatedUser instead. For MT calls, we will
4405      * continue to use the target phone account user (as it's always set) and for MO calls, we will
4406      * use the initiating user instead.
4407      *
4408      * @return user handle of user associated with the call.
4409      */
4410     public UserHandle getAssociatedUser() {
4411         return mAssociatedUser;
4412     }
4413 
4414     /**
4415      * Set the user handle of user associated with the call.
4416      * @param associatedUser
4417      */
4418     public void setAssociatedUser(UserHandle associatedUser) {
4419         Log.i(this, "Setting associated user for call: %s", associatedUser);
4420         Preconditions.checkNotNull(associatedUser);
4421         mAssociatedUser = associatedUser;
4422     }
4423 
4424     static int getStateFromConnectionState(int state) {
4425         switch (state) {
4426             case Connection.STATE_INITIALIZING:
4427                 return CallState.CONNECTING;
4428             case Connection.STATE_ACTIVE:
4429                 return CallState.ACTIVE;
4430             case Connection.STATE_DIALING:
4431                 return CallState.DIALING;
4432             case Connection.STATE_PULLING_CALL:
4433                 return CallState.PULLING;
4434             case Connection.STATE_DISCONNECTED:
4435                 return CallState.DISCONNECTED;
4436             case Connection.STATE_HOLDING:
4437                 return CallState.ON_HOLD;
4438             case Connection.STATE_NEW:
4439                 return CallState.NEW;
4440             case Connection.STATE_RINGING:
4441                 return CallState.RINGING;
4442         }
4443         return CallState.DISCONNECTED;
4444     }
4445 
4446     /**
4447      * Determines if this call is in disconnected state and waiting to be destroyed.
4448      *
4449      * @return {@code true} if this call is disconected.
4450      */
4451     public boolean isDisconnected() {
4452         return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED);
4453     }
4454 
4455     /**
4456      * Determines if this call has just been created and has not been configured properly yet.
4457      *
4458      * @return {@code true} if this call is new.
4459      */
4460     public boolean isNew() {
4461         return getState() == CallState.NEW;
4462     }
4463 
4464     /**
4465      * Sets the call data usage for the call.
4466      *
4467      * @param callDataUsage The new call data usage (in bytes).
4468      */
4469     public void setCallDataUsage(long callDataUsage) {
4470         mCallDataUsage = callDataUsage;
4471     }
4472 
4473     /**
4474      * Returns the call data usage for the call.
4475      *
4476      * @return The call data usage (in bytes).
4477      */
4478     public long getCallDataUsage() {
4479         return mCallDataUsage;
4480     }
4481 
4482     public void setRttMode(int mode) {
4483         mRttMode = mode;
4484         Log.addEvent(this, LogUtils.Events.SET_RRT_MODE, "mode=" + mode);
4485         // TODO: hook this up to CallAudioManager.
4486     }
4487 
4488     /**
4489      * Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent
4490      * has come back to telecom and was processed.
4491      */
4492     public boolean isNewOutgoingCallIntentBroadcastDone() {
4493         return mIsNewOutgoingCallIntentBroadcastDone;
4494     }
4495 
4496     public void setNewOutgoingCallIntentBroadcastIsDone() {
4497         mIsNewOutgoingCallIntentBroadcastDone = true;
4498     }
4499 
4500     /**
4501      * Determines if the call has been held by the remote party.
4502      *
4503      * @return {@code true} if the call is remotely held, {@code false} otherwise.
4504      */
4505     public boolean isRemotelyHeld() {
4506         return mIsRemotelyHeld;
4507     }
4508 
4509     /**
4510      * Handles Connection events received from a {@link ConnectionService}.
4511      *
4512      * @param event The event.
4513      * @param extras The extras.
4514      */
4515     public void onConnectionEvent(String event, Bundle extras) {
4516         if (mIsTransactionalCall) {
4517             // send the Event directly to the ICS via the InCallController listener
4518             for (Listener l : mListeners) {
4519                 l.onConnectionEvent(this, event, extras);
4520             }
4521             // Don't run the below block since it applies to Calls that are attached to a
4522             // ConnectionService
4523             return;
4524         }
4525         // Don't log call quality reports; they're quite frequent and will clog the log.
4526         if (!Connection.EVENT_CALL_QUALITY_REPORT.equals(event)) {
4527             Log.addEvent(this, LogUtils.Events.CONNECTION_EVENT, event);
4528         }
4529         if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) {
4530             mIsRemotelyHeld = true;
4531             Log.addEvent(this, LogUtils.Events.REMOTELY_HELD);
4532             // Inform listeners of the fact that a call hold tone was received.  This will trigger
4533             // the CallAudioManager to play a tone via the InCallTonePlayer.
4534             for (Listener l : mListeners) {
4535                 l.onHoldToneRequested(this);
4536             }
4537         } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) {
4538             mIsRemotelyHeld = false;
4539             Log.addEvent(this, LogUtils.Events.REMOTELY_UNHELD);
4540             for (Listener l : mListeners) {
4541                 l.onHoldToneRequested(this);
4542             }
4543         } else if (Connection.EVENT_CALL_HOLD_FAILED.equals(event)) {
4544             for (Listener l : mListeners) {
4545                 l.onCallHoldFailed(this);
4546             }
4547         } else if (Connection.EVENT_CALL_SWITCH_FAILED.equals(event)) {
4548             for (Listener l : mListeners) {
4549                 l.onCallSwitchFailed(this);
4550             }
4551         } else if (Connection.EVENT_CALL_RESUME_FAILED.equals(event)) {
4552             for (Listener l : mListeners) {
4553                 l.onCallResumeFailed(this);
4554             }
4555         } else if (Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE.equals(event)
4556                 && extras != null && extras.containsKey(
4557                 Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE)
4558                 && extras.containsKey(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE)) {
4559             // Relay an incoming D2D message to interested listeners; most notably the
4560             // CallDiagnosticService.
4561             int messageType = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE);
4562             int messageValue = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE);
4563             for (Listener l : mListeners) {
4564                 l.onReceivedDeviceToDeviceMessage(this, messageType, messageValue);
4565             }
4566         } else if (Connection.EVENT_CALL_QUALITY_REPORT.equals(event)
4567                 && extras != null && extras.containsKey(Connection.EXTRA_CALL_QUALITY_REPORT)) {
4568             CallQuality callQuality = extras.getParcelable(Connection.EXTRA_CALL_QUALITY_REPORT);
4569             for (Listener l : mListeners) {
4570                 l.onReceivedCallQualityReport(this, callQuality);
4571             }
4572         } else {
4573             if (event.equals(EVENT_DISPLAY_EMERGENCY_MESSAGE) && !isEmergencyCall()) {
4574                 Log.w(this, "onConnectionEvent: EVENT_DISPLAY_EMERGENCY_MESSAGE is sent "
4575                         + "without an emergency call");
4576                 return;
4577             }
4578 
4579             for (Listener l : mListeners) {
4580                 l.onConnectionEvent(this, event, extras);
4581             }
4582         }
4583     }
4584 
4585     /**
4586      * Notifies interested parties that the handover has completed.
4587      * Notifies:
4588      * 1. {@link InCallController} which communicates this to the
4589      * {@link android.telecom.InCallService} via {@link Listener#onHandoverComplete()}.
4590      * 2. {@link ConnectionServiceWrapper} which informs the {@link android.telecom.Connection} of
4591      * the successful handover.
4592      */
4593     public void onHandoverComplete() {
4594         Log.i(this, "onHandoverComplete; callId=%s", getId());
4595         if (mConnectionService != null) {
4596             mConnectionService.handoverComplete(this);
4597         }
4598         for (Listener l : mListeners) {
4599             l.onHandoverComplete(this);
4600         }
4601     }
4602 
4603     public void onHandoverFailed(int handoverError) {
4604         Log.i(this, "onHandoverFailed; callId=%s, handoverError=%d", getId(), handoverError);
4605         for (Listener l : mListeners) {
4606             l.onHandoverFailed(this, handoverError);
4607         }
4608     }
4609 
4610     public void setOriginalConnectionId(String originalConnectionId) {
4611         mOriginalConnectionId = originalConnectionId;
4612     }
4613 
4614     /**
4615      * For calls added via a ConnectionManager using the
4616      * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
4617      * Connection)}, or {@link android.telecom.ConnectionService#addConference(Conference)} APIS,
4618      * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
4619      * originally created it.
4620      *
4621      * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
4622      * @return The original connection ID.
4623      */
4624     public String getOriginalConnectionId() {
4625         return mOriginalConnectionId;
4626     }
4627 
4628     public ConnectionServiceFocusManager getConnectionServiceFocusManager() {
4629         return mCallsManager.getConnectionServiceFocusManager();
4630     }
4631 
4632     /**
4633      * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either
4634      * remotely or locally.
4635      *
4636      * @param capabilities The {@link Connection} capabilities for the call.
4637      * @return {@code true} if video is supported, {@code false} otherwise.
4638      */
4639     private boolean doesCallSupportVideo(int capabilities) {
4640         return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 ||
4641                 (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0;
4642     }
4643 
4644     /**
4645      * Remove any video capabilities set on a {@link Connection} capabilities bitmask.
4646      *
4647      * @param capabilities The capabilities.
4648      * @return The bitmask with video capabilities removed.
4649      */
4650     private int removeVideoCapabilities(int capabilities) {
4651         return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL |
4652                 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
4653     }
4654 
4655     /**
4656      * Initiates a handover of this {@link Call} to another {@link PhoneAccount}.
4657      * @param handoverToHandle The {@link PhoneAccountHandle} to handover to.
4658      * @param videoState The video state of the call when handed over.
4659      * @param extras Optional extras {@link Bundle} provided by the initiating
4660      *      {@link android.telecom.InCallService}.
4661      */
4662     private void requestHandover(PhoneAccountHandle handoverToHandle, int videoState,
4663                                  Bundle extras, boolean isLegacy) {
4664         for (Listener l : mListeners) {
4665             l.onHandoverRequested(this, handoverToHandle, videoState, extras, isLegacy);
4666         }
4667     }
4668 
4669     private TelephonyManager getTelephonyManager() {
4670         return mContext.getSystemService(TelephonyManager.class);
4671     }
4672 
4673     /**
4674      * Sets whether this {@link Call} is a conference or not.
4675      * @param isConference
4676      */
4677     public void setConferenceState(boolean isConference) {
4678         mIsConference = isConference;
4679         Log.addEvent(this, LogUtils.Events.CONF_STATE_CHANGED, "isConference=" + isConference);
4680         // Ultimately CallsManager needs to know so it can update the "add call" state and inform
4681         // the UI to update itself.
4682         for (Listener l : mListeners) {
4683             l.onConferenceStateChanged(this, isConference);
4684         }
4685     }
4686 
4687     /**
4688      * Change the call direction. This is useful if it was not previously defined (for example in
4689      * single caller emulation mode).
4690      * @param callDirection The new direction of this call.
4691      */
4692     // Make sure the callDirection has been mapped to the Call definition correctly!
4693     public void setCallDirection(int callDirection) {
4694         if (mCallDirection != callDirection) {
4695             Log.addEvent(this, LogUtils.Events.CALL_DIRECTION_CHANGED, "callDirection="
4696                     + callDirection);
4697             mCallDirection = callDirection;
4698             for (Listener l : mListeners) {
4699                 // Update InCallService directly, do not notify CallsManager.
4700                 l.onCallDirectionChanged(this);
4701             }
4702         }
4703     }
4704 
4705     /**
4706      * Sets the video history based on the state and state transitions of the call. Always add the
4707      * current video state to the video state history during a call transition except for the
4708      * transitions DIALING->ACTIVE and RINGING->ANSWERED. In these cases, clear the history. If a
4709      * call starts dialing/ringing as a VT call and gets downgraded to audio, we need to record
4710      * the history as an audio call.
4711      */
4712     private void updateVideoHistoryViaState(int oldState, int newState) {
4713         if ((oldState == CallState.DIALING && newState == CallState.ACTIVE)
4714                 || (oldState == CallState.RINGING && newState == CallState.ANSWERED)) {
4715             mVideoStateHistory = mVideoState;
4716         }
4717 
4718         mVideoStateHistory |= mVideoState;
4719     }
4720 
4721     /**
4722      * Returns whether or not high definition audio was used.
4723      *
4724      * @return true if high definition audio was used during this call.
4725      */
4726     boolean wasHighDefAudio() {
4727         return mWasHighDefAudio;
4728     }
4729 
4730     /**
4731      * Returns whether or not Wifi call was used.
4732      *
4733      * @return true if wifi call was used during this call.
4734      */
4735     boolean wasWifi() {
4736         return mWasWifi;
4737     }
4738 
4739     public void setIsUsingCallFiltering(boolean isUsingCallFiltering) {
4740         mIsUsingCallFiltering = isUsingCallFiltering;
4741     }
4742 
4743     public boolean isUsingCallFiltering() {
4744         return mIsUsingCallFiltering;
4745     }
4746 
4747     /**
4748      * Returns whether or not Volte call was used.
4749      *
4750      * @return true if Volte call was used during this call.
4751      */
4752     public boolean wasVolte() {
4753         return mWasVolte;
4754     }
4755 
4756     /**
4757      * In some cases, we need to know if this call has ever gone active (for example, the case
4758      * when the call was put into the {@link CallState#AUDIO_PROCESSING} state after being active)
4759      * for call logging purposes.
4760      *
4761      * @return {@code true} if this call has gone active before (even if it isn't now), false if it
4762      * has never gone active.
4763      */
4764     public boolean hasGoneActiveBefore() {
4765         return mHasGoneActiveBefore;
4766     }
4767 
4768     /**
4769      * When upgrading a call to video via
4770      * {@link VideoProviderProxy#onSendSessionModifyRequest(VideoProfile, VideoProfile)}, if the
4771      * upgrade is from audio to video, potentially auto-engage the speakerphone.
4772      * @param newVideoState The proposed new video state for the call.
4773      */
4774     public void maybeEnableSpeakerForVideoUpgrade(@VideoProfile.VideoState int newVideoState) {
4775         if (mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(newVideoState)) {
4776             Log.i(this, "maybeEnableSpeakerForVideoCall; callId=%s, auto-enable speaker for call"
4777                             + " upgraded to video.");
4778             mCallsManager.setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
4779         }
4780     }
4781 
4782     /**
4783      * Sends a device to device message to the other part of the call.
4784      * @param message the message type to send.
4785      * @param value the value for the message.
4786      */
4787     public void sendDeviceToDeviceMessage(@CallDiagnostics.MessageType int message, int value) {
4788         Log.i(this, "sendDeviceToDeviceMessage; callId=%s, msg=%d/%d", getId(), message, value);
4789         Bundle extras = new Bundle();
4790         extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE, message);
4791         extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE, value);
4792         // Send to the connection service.
4793         sendCallEvent(Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE, extras);
4794     }
4795 
4796     /**
4797      * Signals to the Dialer app to start displaying a diagnostic message.
4798      * @param messageId a unique ID for the message to display.
4799      * @param message the message to display.
4800      */
4801     public void displayDiagnosticMessage(int messageId, @NonNull CharSequence message) {
4802         Bundle extras = new Bundle();
4803         extras.putInt(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE_ID, messageId);
4804         extras.putCharSequence(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE, message);
4805         // Send to the dialer.
4806         onConnectionEvent(android.telecom.Call.EVENT_DISPLAY_DIAGNOSTIC_MESSAGE, extras);
4807     }
4808 
4809     /**
4810      * Signals to the Dialer app to stop displaying a diagnostic message.
4811      * @param messageId a unique ID for the message to clear.
4812      */
4813     public void clearDiagnosticMessage(int messageId) {
4814         Bundle extras = new Bundle();
4815         extras.putInt(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE_ID, messageId);
4816         // Send to the dialer.
4817         onConnectionEvent(android.telecom.Call.EVENT_CLEAR_DIAGNOSTIC_MESSAGE, extras);
4818     }
4819 
4820     /**
4821      * Remaps the call direction as indicated by an {@link android.telecom.Call.Details} direction
4822      * constant to the constants (e.g. {@link #CALL_DIRECTION_INCOMING}) used in this call class.
4823      * @param direction The android.telecom.Call direction.
4824      * @return The direction using the constants in this class.
4825      */
4826     public static int getRemappedCallDirection(
4827             @android.telecom.Call.Details.CallDirection int direction) {
4828         switch(direction) {
4829             case android.telecom.Call.Details.DIRECTION_INCOMING:
4830                 return CALL_DIRECTION_INCOMING;
4831             case android.telecom.Call.Details.DIRECTION_OUTGOING:
4832                 return CALL_DIRECTION_OUTGOING;
4833             case android.telecom.Call.Details.DIRECTION_UNKNOWN:
4834                 return CALL_DIRECTION_UNDEFINED;
4835         }
4836         return CALL_DIRECTION_UNDEFINED;
4837     }
4838 
4839     /**
4840      * Set the package name of the {@link android.telecom.CallScreeningService} which should be sent
4841      * the {@link android.telecom.TelecomManager#ACTION_POST_CALL} upon disconnection of a call.
4842      * @param packageName post call screen service package name.
4843      */
4844     public void setPostCallPackageName(String packageName) {
4845         mPostCallPackageName = packageName;
4846     }
4847 
4848     /**
4849      * Return the package name of the {@link android.telecom.CallScreeningService} which should be
4850      * sent the {@link android.telecom.TelecomManager#ACTION_POST_CALL} upon disconnection of a
4851      * call.
4852      * @return post call screen service package name.
4853      */
4854     public String getPostCallPackageName() {
4855         return mPostCallPackageName;
4856     }
4857 
4858     public long getMissedReason() {
4859         return mMissedReason;
4860     }
4861 
4862     public void setMissedReason(long missedReason) {
4863         mMissedReason = missedReason;
4864     }
4865 
4866     public void setUserMissed(long code) {
4867         mMissedReason |= code;
4868     }
4869 
4870     public long getStartRingTime() {
4871         return mStartRingTime;
4872     }
4873 
4874     public void setStartRingTime() {
4875         mStartRingTime = mClockProxy.elapsedRealtime();
4876     }
4877 
4878     public CharSequence getCallScreeningAppName() {
4879         return mCallScreeningAppName;
4880     }
4881 
4882     public void setCallScreeningAppName(CharSequence callScreeningAppName) {
4883         mCallScreeningAppName = callScreeningAppName;
4884     }
4885 
4886     public String getCallScreeningComponentName() {
4887         return mCallScreeningComponentName;
4888     }
4889 
4890     public void setCallScreeningComponentName(String callScreeningComponentName) {
4891         mCallScreeningComponentName = callScreeningComponentName;
4892     }
4893 
4894     public void setStartFailCause(CallFailureCause cause) {
4895         Log.i(this, "setStartFailCause: cause = %s; callId = %s", cause, this.getId());
4896         mCallStateChangedAtomWriter.setStartFailCause(cause);
4897     }
4898 
4899     public void increaseHeldByThisCallCount() {
4900         mCallStateChangedAtomWriter.increaseHeldCallCount();
4901     }
4902 
4903     public void maybeOnInCallServiceTrackingChanged(boolean isTracking, boolean hasUi) {
4904         if (mTransactionalService != null) {
4905             Log.i(this,
4906                     "maybeOnInCallServiceTrackingChanged: called on TransactionalService");
4907             return;
4908         }
4909         if (mConnectionService == null) {
4910             Log.w(this, "maybeOnInCallServiceTrackingChanged() request on a call"
4911                     + " without a connection service.");
4912         } else {
4913             if (hasUi) {
4914                 mConnectionService.onUsingAlternativeUi(this, isTracking);
4915             } else if (isTracking) {
4916                 mConnectionService.onTrackedByNonUiService(this, isTracking);
4917             }
4918         }
4919     }
4920 
4921     /**
4922      * @return {@code true} when this call originated from a SIM-based {@link PhoneAccount}.
4923      * A sim-based {@link PhoneAccount} is one with {@link PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION}
4924      * set.
4925      */
4926     public boolean isSimCall() {
4927         return mIsSimCall;
4928     }
4929 
4930     /**
4931      * Sets whether this is a sim call or not.
4932      * @param isSimCall {@code true} if this is a SIM call, {@code false} otherwise.
4933      */
4934     public void setIsSimCall(boolean isSimCall) {
4935         mIsSimCall = isSimCall;
4936     }
4937 
4938     /**
4939      * Initializes a disconnect future which is used to chain up pending operations which take
4940      * place when the {@link CallDiagnosticService} returns the result of the
4941      * {@link CallDiagnostics#onCallDisconnected(int, int)} or
4942      * {@link CallDiagnostics#onCallDisconnected(ImsReasonInfo)} invocation via
4943      * {@link CallDiagnosticServiceAdapter}.  If no {@link CallDiagnosticService} is in use, we
4944      * would not try to make a disconnect future.
4945      * @param timeoutMillis Timeout we use for waiting for the response.
4946      * @return the {@link CompletableFuture}.
4947      */
4948     public CompletableFuture<Boolean> initializeDiagnosticCompleteFuture(long timeoutMillis) {
4949         if (mDiagnosticCompleteFuture == null) {
4950             mDiagnosticCompleteFuture = new CompletableFuture<Boolean>()
4951                     .completeOnTimeout(false, timeoutMillis, TimeUnit.MILLISECONDS);
4952             // After all the chained stuff we will report where the CDS timed out.
4953             mDiagnosticCompleteFuture.thenRunAsync(() -> {
4954                 if (!mReceivedCallDiagnosticPostCallResponse) {
4955                     Log.addEvent(this, LogUtils.Events.CALL_DIAGNOSTIC_SERVICE_TIMEOUT);
4956                 }
4957                 // Clear the future as a final step.
4958                 mDiagnosticCompleteFuture = null;
4959                 },
4960                 new LoggedHandlerExecutor(mHandler, "C.iDF", mLock))
4961                     .exceptionally((throwable) -> {
4962                         Log.e(this, throwable, "Error while executing disconnect future");
4963                         return null;
4964                     });
4965         }
4966         return mDiagnosticCompleteFuture;
4967     }
4968 
4969     /**
4970      * @return the disconnect future, if initialized.  Used for chaining operations after creation.
4971      */
4972     public CompletableFuture<Boolean> getDiagnosticCompleteFuture() {
4973         return mDiagnosticCompleteFuture;
4974     }
4975 
4976     /**
4977      * @return {@code true} if disconnection and removal is handled via a future, or {@code false}
4978      * if this is handled immediately.
4979      */
4980     public boolean isDisconnectHandledViaFuture() {
4981         return mDiagnosticCompleteFuture != null;
4982     }
4983 
4984     /**
4985      * Perform any cleanup on this call as a result of a {@link TelecomServiceImpl}
4986      * {@code cleanupStuckCalls} request.
4987      */
4988     public void cleanup() {
4989         if (mDiagnosticCompleteFuture != null) {
4990             mDiagnosticCompleteFuture.complete(false);
4991             mDiagnosticCompleteFuture = null;
4992         }
4993     }
4994 
4995     /**
4996      * Set the pending future to use when the call is disconnected.
4997      */
4998     public void setDisconnectFuture(CompletableFuture<Void> future) {
4999         mDisconnectFuture = future;
5000     }
5001 
5002     /**
5003      * @return The future that will be executed when the call is disconnected.
5004      */
5005     public CompletableFuture<Void> getDisconnectFuture() {
5006         return mDisconnectFuture;
5007     }
5008 
5009     /**
5010      * Set the future that will be used when call removal is taking place.
5011      */
5012     public void setRemovalFuture(CompletableFuture<Void> future) {
5013         mRemovalFuture = future;
5014     }
5015 
5016     /**
5017      * @return {@code true} if there is a pending removal operation that hasn't taken place yet, or
5018      * {@code false} if there is no removal pending.
5019      */
5020     public boolean isRemovalPending() {
5021         return mRemovalFuture != null && !mRemovalFuture.isDone();
5022     }
5023 
5024     /**
5025      * Set the bluetooth {@link android.telecom.InCallService} binding completion or timeout future
5026      * which is used to delay the audio routing change after the bluetooth stack get notified about
5027      * the ringing calls.
5028      * @param btIcsFuture the {@link CompletableFuture}
5029      */
5030     public void setBtIcsFuture(CompletableFuture<Boolean> btIcsFuture) {
5031         mBtIcsFuture = btIcsFuture;
5032     }
5033 
5034     /**
5035      * @return The binding {@link CompletableFuture} for the BT ICS.
5036      */
5037     public CompletableFuture<Boolean> getBtIcsFuture() {
5038         return mBtIcsFuture;
5039     }
5040 
5041     /**
5042      * Wait for bluetooth {@link android.telecom.InCallService} binding completion or timeout. Used
5043      * for audio routing operations for a ringing call.
5044      */
5045     public void waitForBtIcs() {
5046         if (mBtIcsFuture != null) {
5047             try {
5048                 Log.i(this, "waitForBtIcs: waiting for BT service to bind");
5049                 mBtIcsFuture.get();
5050             } catch (InterruptedException | ExecutionException e) {
5051                 // ignore
5052             }
5053         }
5054     }
5055 
5056     /**
5057      * @return {@code true} if the connection has been created by the underlying
5058      * {@link ConnectionService}, {@code false} otherwise.
5059      */
5060     public boolean isCreateConnectionComplete() {
5061         return mIsCreateConnectionComplete;
5062     }
5063 
5064     @VisibleForTesting
5065     public void setIsCreateConnectionComplete(boolean isCreateConnectionComplete) {
5066         mIsCreateConnectionComplete = isCreateConnectionComplete;
5067     }
5068 
5069     public boolean isStreaming() {
5070         synchronized (mLock) {
5071             return mIsStreaming;
5072         }
5073     }
5074 
5075     public void startStreaming() {
5076         if (!mIsTransactionalCall) {
5077             throw new UnsupportedOperationException(
5078                     "Can't streaming call created by non voip apps");
5079         }
5080         Log.addEvent(this, LogUtils.Events.START_STREAMING);
5081         synchronized (mLock) {
5082             if (mIsStreaming) {
5083                 // ignore
5084                 return;
5085             }
5086 
5087             mIsStreaming = true;
5088             for (Listener listener : mListeners) {
5089                 listener.onCallStreamingStateChanged(this, true /** isStreaming */);
5090             }
5091         }
5092     }
5093 
5094     public void stopStreaming() {
5095         synchronized (mLock) {
5096             if (!mIsStreaming) {
5097                 // ignore
5098                 return;
5099             }
5100             Log.addEvent(this, LogUtils.Events.STOP_STREAMING);
5101             mIsStreaming = false;
5102             for (Listener listener : mListeners) {
5103                 listener.onCallStreamingStateChanged(this, false /** isStreaming */);
5104             }
5105         }
5106     }
5107 
5108     public void setSimultaneousType(int simultaneousType) {
5109         mSimultaneousType = simultaneousType;
5110     }
5111 
5112     public int getSimultaneousType() {
5113         return mSimultaneousType;
5114     }
5115 
5116     public boolean hasVideoCall() {
5117         return mHasVideoCall;
5118     }
5119 
5120     /**
5121      * Used only for call sequencing for cases when we may end up auto-unholding the held call while
5122      * processing an outgoing (emergency) call. We want to refrain from unholding the held call so
5123      * that we don't end up with two active calls. Once the outgoing call is disconnected (either
5124      * from a successful disconnect by the user or a failed call), the auto-unhold logic will be
5125      * triggered again and successfully unhold the held call at that point. Note, that this only
5126      * applies to non-holdable phone accounts (i.e. Verizon). Refer to
5127      * {@link CallsManagerCallSequencingAdapter#maybeMoveHeldCallToForeground} for details.
5128      */
5129     public void setSkipAutoUnhold(boolean result) {
5130         mSkipAutoUnhold = result;
5131     }
5132 
5133     public boolean getSkipAutoUnhold() {
5134         return mSkipAutoUnhold;
5135     }
5136 }
5137