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