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