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