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