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