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