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