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