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