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