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.InCallService.VideoCall;
     33 import android.telecom.ParcelableConnection;
     34 import android.telecom.PhoneAccount;
     35 import android.telecom.PhoneAccountHandle;
     36 import android.telecom.Response;
     37 import android.telecom.StatusHints;
     38 import android.telecom.TelecomManager;
     39 import android.telecom.VideoProfile;
     40 import android.telephony.PhoneNumberUtils;
     41 import android.text.TextUtils;
     42 
     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.CallerInfoAsyncQuery.OnQueryCompleteListener;
     47 import com.android.internal.telephony.SmsApplication;
     48 import com.android.server.telecom.ContactsAsyncHelper.OnImageLoadCompleteListener;
     49 import com.android.internal.util.Preconditions;
     50 
     51 import java.util.ArrayList;
     52 import java.util.Collections;
     53 import java.util.LinkedList;
     54 import java.util.List;
     55 import java.util.Locale;
     56 import java.util.Objects;
     57 import java.util.Set;
     58 import java.util.concurrent.ConcurrentHashMap;
     59 
     60 /**
     61  *  Encapsulates all aspects of a given phone call throughout its lifecycle, starting
     62  *  from the time the call intent was received by Telecom (vs. the time the call was
     63  *  connected etc).
     64  */
     65 @VisibleForTesting
     66 public class Call implements CreateConnectionResponse {
     67     /**
     68      * Listener for events on the call.
     69      */
     70     interface Listener {
     71         void onSuccessfulOutgoingCall(Call call, int callState);
     72         void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause);
     73         void onSuccessfulIncomingCall(Call call);
     74         void onFailedIncomingCall(Call call);
     75         void onSuccessfulUnknownCall(Call call, int callState);
     76         void onFailedUnknownCall(Call call);
     77         void onRingbackRequested(Call call, boolean ringbackRequested);
     78         void onPostDialWait(Call call, String remaining);
     79         void onPostDialChar(Call call, char nextChar);
     80         void onConnectionCapabilitiesChanged(Call call);
     81         void onParentChanged(Call call);
     82         void onChildrenChanged(Call call);
     83         void onCannedSmsResponsesLoaded(Call call);
     84         void onVideoCallProviderChanged(Call call);
     85         void onCallerInfoChanged(Call call);
     86         void onIsVoipAudioModeChanged(Call call);
     87         void onStatusHintsChanged(Call call);
     88         void onExtrasChanged(Call call);
     89         void onHandleChanged(Call call);
     90         void onCallerDisplayNameChanged(Call call);
     91         void onVideoStateChanged(Call call);
     92         void onTargetPhoneAccountChanged(Call call);
     93         void onConnectionManagerPhoneAccountChanged(Call call);
     94         void onPhoneAccountChanged(Call call);
     95         void onConferenceableCallsChanged(Call call);
     96         boolean onCanceledViaNewOutgoingCallBroadcast(Call call);
     97     }
     98 
     99     public abstract static class ListenerBase implements Listener {
    100         @Override
    101         public void onSuccessfulOutgoingCall(Call call, int callState) {}
    102         @Override
    103         public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {}
    104         @Override
    105         public void onSuccessfulIncomingCall(Call call) {}
    106         @Override
    107         public void onFailedIncomingCall(Call call) {}
    108         @Override
    109         public void onSuccessfulUnknownCall(Call call, int callState) {}
    110         @Override
    111         public void onFailedUnknownCall(Call call) {}
    112         @Override
    113         public void onRingbackRequested(Call call, boolean ringbackRequested) {}
    114         @Override
    115         public void onPostDialWait(Call call, String remaining) {}
    116         @Override
    117         public void onPostDialChar(Call call, char nextChar) {}
    118         @Override
    119         public void onConnectionCapabilitiesChanged(Call call) {}
    120         @Override
    121         public void onParentChanged(Call call) {}
    122         @Override
    123         public void onChildrenChanged(Call call) {}
    124         @Override
    125         public void onCannedSmsResponsesLoaded(Call call) {}
    126         @Override
    127         public void onVideoCallProviderChanged(Call call) {}
    128         @Override
    129         public void onCallerInfoChanged(Call call) {}
    130         @Override
    131         public void onIsVoipAudioModeChanged(Call call) {}
    132         @Override
    133         public void onStatusHintsChanged(Call call) {}
    134         @Override
    135         public void onExtrasChanged(Call call) {}
    136         @Override
    137         public void onHandleChanged(Call call) {}
    138         @Override
    139         public void onCallerDisplayNameChanged(Call call) {}
    140         @Override
    141         public void onVideoStateChanged(Call call) {}
    142         @Override
    143         public void onTargetPhoneAccountChanged(Call call) {}
    144         @Override
    145         public void onConnectionManagerPhoneAccountChanged(Call call) {}
    146         @Override
    147         public void onPhoneAccountChanged(Call call) {}
    148         @Override
    149         public void onConferenceableCallsChanged(Call call) {}
    150         @Override
    151         public boolean onCanceledViaNewOutgoingCallBroadcast(Call call) {
    152             return false;
    153         }
    154     }
    155 
    156     private final OnQueryCompleteListener mCallerInfoQueryListener =
    157             new OnQueryCompleteListener() {
    158                 /** ${inheritDoc} */
    159                 @Override
    160                 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
    161                     synchronized (mLock) {
    162                         if (cookie != null) {
    163                             ((Call) cookie).setCallerInfo(callerInfo, token);
    164                         }
    165                     }
    166                 }
    167             };
    168 
    169     private final OnImageLoadCompleteListener mPhotoLoadListener =
    170             new OnImageLoadCompleteListener() {
    171                 /** ${inheritDoc} */
    172                 @Override
    173                 public void onImageLoadComplete(
    174                         int token, Drawable photo, Bitmap photoIcon, Object cookie) {
    175                     synchronized (mLock) {
    176                         if (cookie != null) {
    177                             ((Call) cookie).setPhoto(photo, photoIcon, token);
    178                         }
    179                     }
    180                 }
    181             };
    182 
    183     private final Runnable mDirectToVoicemailRunnable = new Runnable() {
    184         @Override
    185         public void run() {
    186             synchronized (mLock) {
    187                 processDirectToVoicemail();
    188             }
    189         }
    190     };
    191 
    192     /** True if this is an incoming call. */
    193     private final boolean mIsIncoming;
    194 
    195     /** True if this is a currently unknown call that was not previously tracked by CallsManager,
    196      *  and did not originate via the regular incoming/outgoing call code paths.
    197      */
    198     private boolean mIsUnknown;
    199 
    200     /**
    201      * The time this call was created. Beyond logging and such, may also be used for bookkeeping
    202      * and specifically for marking certain call attempts as failed attempts.
    203      */
    204     private long mCreationTimeMillis = System.currentTimeMillis();
    205 
    206     /** The time this call was made active. */
    207     private long mConnectTimeMillis = 0;
    208 
    209     /** The time this call was disconnected. */
    210     private long mDisconnectTimeMillis = 0;
    211 
    212     /** The gateway information associated with this call. This stores the original call handle
    213      * that the user is attempting to connect to via the gateway, the actual handle to dial in
    214      * order to connect the call via the gateway, as well as the package name of the gateway
    215      * service. */
    216     private GatewayInfo mGatewayInfo;
    217 
    218     private PhoneAccountHandle mConnectionManagerPhoneAccountHandle;
    219 
    220     private PhoneAccountHandle mTargetPhoneAccountHandle;
    221 
    222     private final Handler mHandler = new Handler(Looper.getMainLooper());
    223 
    224     private final List<Call> mConferenceableCalls = new ArrayList<>();
    225 
    226     /** The state of the call. */
    227     private int mState;
    228 
    229     /** The handle with which to establish this call. */
    230     private Uri mHandle;
    231 
    232     /**
    233      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
    234      */
    235     private int mHandlePresentation;
    236 
    237     /** The caller display name (CNAP) set by the connection service. */
    238     private String mCallerDisplayName;
    239 
    240     /**
    241      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
    242      */
    243     private int mCallerDisplayNamePresentation;
    244 
    245     /**
    246      * The connection service which is attempted or already connecting this call.
    247      */
    248     private ConnectionServiceWrapper mConnectionService;
    249 
    250     private boolean mIsEmergencyCall;
    251 
    252     private boolean mSpeakerphoneOn;
    253 
    254     /**
    255      * Tracks the video states which were applicable over the duration of a call.
    256      * See {@link VideoProfile} for a list of valid video states.
    257      * <p>
    258      * Video state history is tracked when the call is active, and when a call is rejected or
    259      * missed.
    260      */
    261     private int mVideoStateHistory;
    262 
    263     private int mVideoState;
    264 
    265     /**
    266      * Disconnect cause for the call. Only valid if the state of the call is STATE_DISCONNECTED.
    267      * See {@link android.telecom.DisconnectCause}.
    268      */
    269     private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);
    270 
    271     private Bundle mIntentExtras = new Bundle();
    272 
    273     /** Set of listeners on this call.
    274      *
    275      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
    276      * load factor before resizing, 1 means we only expect a single thread to
    277      * access the map so make only a single shard
    278      */
    279     private final Set<Listener> mListeners = Collections.newSetFromMap(
    280             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
    281 
    282     private CreateConnectionProcessor mCreateConnectionProcessor;
    283 
    284     /** Caller information retrieved from the latest contact query. */
    285     private CallerInfo mCallerInfo;
    286 
    287     /** The latest token used with a contact info query. */
    288     private int mQueryToken = 0;
    289 
    290     /** Whether this call is requesting that Telecom play the ringback tone on its behalf. */
    291     private boolean mRingbackRequested = false;
    292 
    293     /** Whether direct-to-voicemail query is pending. */
    294     private boolean mDirectToVoicemailQueryPending;
    295 
    296     private int mConnectionCapabilities;
    297 
    298     private boolean mIsConference = false;
    299 
    300     private Call mParentCall = null;
    301 
    302     private List<Call> mChildCalls = new LinkedList<>();
    303 
    304     /** Set of text message responses allowed for this call, if applicable. */
    305     private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
    306 
    307     /** Whether an attempt has been made to load the text message responses. */
    308     private boolean mCannedSmsResponsesLoadingStarted = false;
    309 
    310     private IVideoProvider mVideoProvider;
    311     private VideoProviderProxy mVideoProviderProxy;
    312 
    313     private boolean mIsVoipAudioMode;
    314     private StatusHints mStatusHints;
    315     private Bundle mExtras;
    316     private final ConnectionServiceRepository mRepository;
    317     private final ContactsAsyncHelper mContactsAsyncHelper;
    318     private final Context mContext;
    319     private final CallsManager mCallsManager;
    320     private final TelecomSystem.SyncRoot mLock;
    321     private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
    322 
    323     private boolean mWasConferencePreviouslyMerged = false;
    324 
    325     // For conferences which support merge/swap at their level, we retain a notion of an active
    326     // call. This is used for BluetoothPhoneService.  In order to support hold/merge, it must have
    327     // the notion of the current "active" call within the conference call. This maintains the
    328     // "active" call and switches every time the user hits "swap".
    329     private Call mConferenceLevelActiveCall = null;
    330 
    331     private boolean mIsLocallyDisconnecting = false;
    332 
    333     /**
    334      * Persists the specified parameters and initializes the new instance.
    335      *
    336      * @param context The context.
    337      * @param repository The connection service repository.
    338      * @param handle The handle to dial.
    339      * @param gatewayInfo Gateway information to use for the call.
    340      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
    341      *         This account must be one that was registered with the
    342      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
    343      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
    344      *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
    345      * @param isIncoming True if this is an incoming call.
    346      */
    347     public Call(
    348             Context context,
    349             CallsManager callsManager,
    350             TelecomSystem.SyncRoot lock,
    351             ConnectionServiceRepository repository,
    352             ContactsAsyncHelper contactsAsyncHelper,
    353             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
    354             Uri handle,
    355             GatewayInfo gatewayInfo,
    356             PhoneAccountHandle connectionManagerPhoneAccountHandle,
    357             PhoneAccountHandle targetPhoneAccountHandle,
    358             boolean isIncoming,
    359             boolean isConference) {
    360         mState = isConference ? CallState.ACTIVE : CallState.NEW;
    361         mContext = context;
    362         mCallsManager = callsManager;
    363         mLock = lock;
    364         mRepository = repository;
    365         mContactsAsyncHelper = contactsAsyncHelper;
    366         mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
    367         setHandle(handle);
    368         setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
    369         mGatewayInfo = gatewayInfo;
    370         setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
    371         setTargetPhoneAccount(targetPhoneAccountHandle);
    372         mIsIncoming = isIncoming;
    373         mIsConference = isConference;
    374         maybeLoadCannedSmsResponses();
    375 
    376         Log.event(this, Log.Events.CREATED);
    377     }
    378 
    379     /**
    380      * Persists the specified parameters and initializes the new instance.
    381      *
    382      * @param context The context.
    383      * @param repository The connection service repository.
    384      * @param handle The handle to dial.
    385      * @param gatewayInfo Gateway information to use for the call.
    386      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
    387      *         This account must be one that was registered with the
    388      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
    389      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
    390      *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
    391      * @param isIncoming True if this is an incoming call.
    392      * @param connectTimeMillis The connection time of the call.
    393      */
    394     Call(
    395             Context context,
    396             CallsManager callsManager,
    397             TelecomSystem.SyncRoot lock,
    398             ConnectionServiceRepository repository,
    399             ContactsAsyncHelper contactsAsyncHelper,
    400             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
    401             Uri handle,
    402             GatewayInfo gatewayInfo,
    403             PhoneAccountHandle connectionManagerPhoneAccountHandle,
    404             PhoneAccountHandle targetPhoneAccountHandle,
    405             boolean isIncoming,
    406             boolean isConference,
    407             long connectTimeMillis) {
    408         this(context, callsManager, lock, repository, contactsAsyncHelper,
    409                 callerInfoAsyncQueryFactory, handle, gatewayInfo,
    410                 connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, isIncoming,
    411                 isConference);
    412 
    413         mConnectTimeMillis = connectTimeMillis;
    414     }
    415 
    416     public void addListener(Listener listener) {
    417         mListeners.add(listener);
    418     }
    419 
    420     public void removeListener(Listener listener) {
    421         if (listener != null) {
    422             mListeners.remove(listener);
    423         }
    424     }
    425 
    426     public void destroy() {
    427         Log.event(this, Log.Events.DESTROYED);
    428     }
    429 
    430     /** {@inheritDoc} */
    431     @Override
    432     public String toString() {
    433         String component = null;
    434         if (mConnectionService != null && mConnectionService.getComponentName() != null) {
    435             component = mConnectionService.getComponentName().flattenToShortString();
    436         }
    437 
    438 
    439 
    440         return String.format(Locale.US, "[%s, %s, %s, %s, %s, childs(%d), has_parent(%b), [%s]]",
    441                 System.identityHashCode(this),
    442                 CallState.toString(mState),
    443                 component,
    444                 Log.piiHandle(mHandle),
    445                 getVideoStateDescription(getVideoState()),
    446                 getChildCalls().size(),
    447                 getParentCall() != null,
    448                 Connection.capabilitiesToString(getConnectionCapabilities()));
    449     }
    450 
    451     /**
    452      * Builds a debug-friendly description string for a video state.
    453      * <p>
    454      * A = audio active, T = video transmission active, R = video reception active, P = video
    455      * paused.
    456      *
    457      * @param videoState The video state.
    458      * @return A string indicating which bits are set in the video state.
    459      */
    460     private String getVideoStateDescription(int videoState) {
    461         StringBuilder sb = new StringBuilder();
    462         sb.append("A");
    463 
    464         if (VideoProfile.isTransmissionEnabled(videoState)) {
    465             sb.append("T");
    466         }
    467 
    468         if (VideoProfile.isReceptionEnabled(videoState)) {
    469             sb.append("R");
    470         }
    471 
    472         if (VideoProfile.isPaused(videoState)) {
    473             sb.append("P");
    474         }
    475 
    476         return sb.toString();
    477     }
    478 
    479     int getState() {
    480         return mState;
    481     }
    482 
    483     private boolean shouldContinueProcessingAfterDisconnect() {
    484         // Stop processing once the call is active.
    485         if (!CreateConnectionTimeout.isCallBeingPlaced(this)) {
    486             return false;
    487         }
    488 
    489         // Make sure that there are additional connection services to process.
    490         if (mCreateConnectionProcessor == null
    491             || !mCreateConnectionProcessor.isProcessingComplete()
    492             || !mCreateConnectionProcessor.hasMorePhoneAccounts()) {
    493             return false;
    494         }
    495 
    496         if (mDisconnectCause == null) {
    497             return false;
    498         }
    499 
    500         // Continue processing if the current attempt failed or timed out.
    501         return mDisconnectCause.getCode() == DisconnectCause.ERROR ||
    502             mCreateConnectionProcessor.isCallTimedOut();
    503     }
    504 
    505     /**
    506      * Sets the call state. Although there exists the notion of appropriate state transitions
    507      * (see {@link CallState}), in practice those expectations break down when cellular systems
    508      * misbehave and they do this very often. The result is that we do not enforce state transitions
    509      * and instead keep the code resilient to unexpected state changes.
    510      */
    511     public void setState(int newState, String tag) {
    512         if (mState != newState) {
    513             Log.v(this, "setState %s -> %s", mState, newState);
    514 
    515             if (newState == CallState.DISCONNECTED && shouldContinueProcessingAfterDisconnect()) {
    516                 Log.w(this, "continuing processing disconnected call with another service");
    517                 mCreateConnectionProcessor.continueProcessingIfPossible(this, mDisconnectCause);
    518                 return;
    519             }
    520 
    521             mState = newState;
    522             maybeLoadCannedSmsResponses();
    523 
    524             if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
    525                 if (mConnectTimeMillis == 0) {
    526                     // We check to see if mConnectTime is already set to prevent the
    527                     // call from resetting active time when it goes in and out of
    528                     // ACTIVE/ON_HOLD
    529                     mConnectTimeMillis = System.currentTimeMillis();
    530                 }
    531 
    532                 // Video state changes are normally tracked against history when a call is active.
    533                 // When the call goes active we need to be sure we track the history in case the
    534                 // state never changes during the duration of the call -- we want to ensure we
    535                 // always know the state at the start of the call.
    536                 mVideoStateHistory = mVideoStateHistory | mVideoState;
    537 
    538                 // We're clearly not disconnected, so reset the disconnected time.
    539                 mDisconnectTimeMillis = 0;
    540             } else if (mState == CallState.DISCONNECTED) {
    541                 mDisconnectTimeMillis = System.currentTimeMillis();
    542                 setLocallyDisconnecting(false);
    543                 fixParentAfterDisconnect();
    544             }
    545             if (mState == CallState.DISCONNECTED &&
    546                     mDisconnectCause.getCode() == DisconnectCause.MISSED) {
    547                 // Ensure when an incoming call is missed that the video state history is updated.
    548                 mVideoStateHistory |= mVideoState;
    549             }
    550 
    551             // Log the state transition event
    552             String event = null;
    553             Object data = null;
    554             switch (newState) {
    555                 case CallState.ACTIVE:
    556                     event = Log.Events.SET_ACTIVE;
    557                     break;
    558                 case CallState.CONNECTING:
    559                     event = Log.Events.SET_CONNECTING;
    560                     break;
    561                 case CallState.DIALING:
    562                     event = Log.Events.SET_DIALING;
    563                     break;
    564                 case CallState.DISCONNECTED:
    565                     event = Log.Events.SET_DISCONNECTED;
    566                     data = getDisconnectCause();
    567                     break;
    568                 case CallState.DISCONNECTING:
    569                     event = Log.Events.SET_DISCONNECTING;
    570                     break;
    571                 case CallState.ON_HOLD:
    572                     event = Log.Events.SET_HOLD;
    573                     break;
    574                 case CallState.SELECT_PHONE_ACCOUNT:
    575                     event = Log.Events.SET_SELECT_PHONE_ACCOUNT;
    576                     break;
    577                 case CallState.RINGING:
    578                     event = Log.Events.SET_RINGING;
    579                     break;
    580             }
    581             if (event != null) {
    582                 // The string data should be just the tag.
    583                 String stringData = tag;
    584                 if (data != null) {
    585                     // If data exists, add it to tag.  If no tag, just use data.toString().
    586                     stringData = stringData == null ? data.toString() : stringData + "> " + data;
    587                 }
    588                 Log.event(this, event, stringData);
    589             }
    590         }
    591     }
    592 
    593     void setRingbackRequested(boolean ringbackRequested) {
    594         mRingbackRequested = ringbackRequested;
    595         for (Listener l : mListeners) {
    596             l.onRingbackRequested(this, mRingbackRequested);
    597         }
    598     }
    599 
    600     boolean isRingbackRequested() {
    601         return mRingbackRequested;
    602     }
    603 
    604     boolean isConference() {
    605         return mIsConference;
    606     }
    607 
    608     public Uri getHandle() {
    609         return mHandle;
    610     }
    611 
    612     int getHandlePresentation() {
    613         return mHandlePresentation;
    614     }
    615 
    616 
    617     void setHandle(Uri handle) {
    618         setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
    619     }
    620 
    621     public void setHandle(Uri handle, int presentation) {
    622         if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
    623             mHandlePresentation = presentation;
    624             if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED ||
    625                     mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) {
    626                 mHandle = null;
    627             } else {
    628                 mHandle = handle;
    629                 if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme())
    630                         && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) {
    631                     // If the number is actually empty, set it to null, unless this is a
    632                     // SCHEME_VOICEMAIL uri which always has an empty number.
    633                     mHandle = null;
    634                 }
    635             }
    636 
    637             mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(mContext,
    638                     mHandle.getSchemeSpecificPart());
    639             startCallerInfoLookup();
    640             for (Listener l : mListeners) {
    641                 l.onHandleChanged(this);
    642             }
    643         }
    644     }
    645 
    646     String getCallerDisplayName() {
    647         return mCallerDisplayName;
    648     }
    649 
    650     int getCallerDisplayNamePresentation() {
    651         return mCallerDisplayNamePresentation;
    652     }
    653 
    654     void setCallerDisplayName(String callerDisplayName, int presentation) {
    655         if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
    656                 presentation != mCallerDisplayNamePresentation) {
    657             mCallerDisplayName = callerDisplayName;
    658             mCallerDisplayNamePresentation = presentation;
    659             for (Listener l : mListeners) {
    660                 l.onCallerDisplayNameChanged(this);
    661             }
    662         }
    663     }
    664 
    665     public String getName() {
    666         return mCallerInfo == null ? null : mCallerInfo.name;
    667     }
    668 
    669     public Bitmap getPhotoIcon() {
    670         return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
    671     }
    672 
    673     public Drawable getPhoto() {
    674         return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
    675     }
    676 
    677     /**
    678      * @param disconnectCause The reason for the disconnection, represented by
    679      *         {@link android.telecom.DisconnectCause}.
    680      */
    681     public void setDisconnectCause(DisconnectCause disconnectCause) {
    682         // TODO: Consider combining this method with a setDisconnected() method that is totally
    683         // separate from setState.
    684         mDisconnectCause = disconnectCause;
    685     }
    686 
    687     public DisconnectCause getDisconnectCause() {
    688         return mDisconnectCause;
    689     }
    690 
    691     boolean isEmergencyCall() {
    692         return mIsEmergencyCall;
    693     }
    694 
    695     /**
    696      * @return The original handle this call is associated with. In-call services should use this
    697      * handle when indicating in their UI the handle that is being called.
    698      */
    699     public Uri getOriginalHandle() {
    700         if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
    701             return mGatewayInfo.getOriginalAddress();
    702         }
    703         return getHandle();
    704     }
    705 
    706     GatewayInfo getGatewayInfo() {
    707         return mGatewayInfo;
    708     }
    709 
    710     void setGatewayInfo(GatewayInfo gatewayInfo) {
    711         mGatewayInfo = gatewayInfo;
    712     }
    713 
    714     PhoneAccountHandle getConnectionManagerPhoneAccount() {
    715         return mConnectionManagerPhoneAccountHandle;
    716     }
    717 
    718     void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
    719         if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
    720             mConnectionManagerPhoneAccountHandle = accountHandle;
    721             for (Listener l : mListeners) {
    722                 l.onConnectionManagerPhoneAccountChanged(this);
    723             }
    724         }
    725 
    726     }
    727 
    728     PhoneAccountHandle getTargetPhoneAccount() {
    729         return mTargetPhoneAccountHandle;
    730     }
    731 
    732     void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
    733         if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
    734             mTargetPhoneAccountHandle = accountHandle;
    735             for (Listener l : mListeners) {
    736                 l.onTargetPhoneAccountChanged(this);
    737             }
    738         }
    739     }
    740 
    741     boolean isIncoming() {
    742         return mIsIncoming;
    743     }
    744 
    745     /**
    746      * @return The "age" of this call object in milliseconds, which typically also represents the
    747      *     period since this call was added to the set pending outgoing calls, see
    748      *     mCreationTimeMillis.
    749      */
    750     long getAgeMillis() {
    751         if (mState == CallState.DISCONNECTED &&
    752                 (mDisconnectCause.getCode() == DisconnectCause.REJECTED ||
    753                  mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
    754             // Rejected and missed calls have no age. They're immortal!!
    755             return 0;
    756         } else if (mConnectTimeMillis == 0) {
    757             // Age is measured in the amount of time the call was active. A zero connect time
    758             // indicates that we never went active, so return 0 for the age.
    759             return 0;
    760         } else if (mDisconnectTimeMillis == 0) {
    761             // We connected, but have not yet disconnected
    762             return System.currentTimeMillis() - mConnectTimeMillis;
    763         }
    764 
    765         return mDisconnectTimeMillis - mConnectTimeMillis;
    766     }
    767 
    768     /**
    769      * @return The time when this call object was created and added to the set of pending outgoing
    770      *     calls.
    771      */
    772     public long getCreationTimeMillis() {
    773         return mCreationTimeMillis;
    774     }
    775 
    776     public void setCreationTimeMillis(long time) {
    777         mCreationTimeMillis = time;
    778     }
    779 
    780     long getConnectTimeMillis() {
    781         return mConnectTimeMillis;
    782     }
    783 
    784     int getConnectionCapabilities() {
    785         return mConnectionCapabilities;
    786     }
    787 
    788     void setConnectionCapabilities(int connectionCapabilities) {
    789         setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */);
    790     }
    791 
    792     void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) {
    793         Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString(
    794                 connectionCapabilities));
    795         if (forceUpdate || mConnectionCapabilities != connectionCapabilities) {
    796            mConnectionCapabilities = connectionCapabilities;
    797             for (Listener l : mListeners) {
    798                 l.onConnectionCapabilitiesChanged(this);
    799             }
    800         }
    801     }
    802 
    803     Call getParentCall() {
    804         return mParentCall;
    805     }
    806 
    807     List<Call> getChildCalls() {
    808         return mChildCalls;
    809     }
    810 
    811     boolean wasConferencePreviouslyMerged() {
    812         return mWasConferencePreviouslyMerged;
    813     }
    814 
    815     Call getConferenceLevelActiveCall() {
    816         return mConferenceLevelActiveCall;
    817     }
    818 
    819     ConnectionServiceWrapper getConnectionService() {
    820         return mConnectionService;
    821     }
    822 
    823     /**
    824      * Retrieves the {@link Context} for the call.
    825      *
    826      * @return The {@link Context}.
    827      */
    828     Context getContext() {
    829         return mContext;
    830     }
    831 
    832     void setConnectionService(ConnectionServiceWrapper service) {
    833         Preconditions.checkNotNull(service);
    834 
    835         clearConnectionService();
    836 
    837         service.incrementAssociatedCallCount();
    838         mConnectionService = service;
    839         mConnectionService.addCall(this);
    840     }
    841 
    842     /**
    843      * Clears the associated connection service.
    844      */
    845     void clearConnectionService() {
    846         if (mConnectionService != null) {
    847             ConnectionServiceWrapper serviceTemp = mConnectionService;
    848             mConnectionService = null;
    849             serviceTemp.removeCall(this);
    850 
    851             // Decrementing the count can cause the service to unbind, which itself can trigger the
    852             // service-death code.  Since the service death code tries to clean up any associated
    853             // calls, we need to make sure to remove that information (e.g., removeCall()) before
    854             // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
    855             // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
    856             // to do.
    857             decrementAssociatedCallCount(serviceTemp);
    858         }
    859     }
    860 
    861     private void processDirectToVoicemail() {
    862         if (mDirectToVoicemailQueryPending) {
    863             if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
    864                 Log.i(this, "Directing call to voicemail: %s.", this);
    865                 // TODO: Once we move State handling from CallsManager to Call, we
    866                 // will not need to set STATE_RINGING state prior to calling reject.
    867                 setState(CallState.RINGING, "directing to voicemail");
    868                 reject(false, null);
    869             } else {
    870                 // TODO: Make this class (not CallsManager) responsible for changing
    871                 // the call state to STATE_RINGING.
    872 
    873                 // TODO: Replace this with state transition to STATE_RINGING.
    874                 for (Listener l : mListeners) {
    875                     l.onSuccessfulIncomingCall(this);
    876                 }
    877             }
    878 
    879             mDirectToVoicemailQueryPending = false;
    880         }
    881     }
    882 
    883     /**
    884      * Starts the create connection sequence. Upon completion, there should exist an active
    885      * connection through a connection service (or the call will have failed).
    886      *
    887      * @param phoneAccountRegistrar The phone account registrar.
    888      */
    889     void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
    890         Preconditions.checkState(mCreateConnectionProcessor == null);
    891         mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
    892                 phoneAccountRegistrar, mContext);
    893         mCreateConnectionProcessor.process();
    894     }
    895 
    896     @Override
    897     public void handleCreateConnectionSuccess(
    898             CallIdMapper idMapper,
    899             ParcelableConnection connection) {
    900         Log.v(this, "handleCreateConnectionSuccessful %s", connection);
    901         setTargetPhoneAccount(connection.getPhoneAccount());
    902         setHandle(connection.getHandle(), connection.getHandlePresentation());
    903         setCallerDisplayName(
    904                 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
    905         setConnectionCapabilities(connection.getConnectionCapabilities());
    906         setVideoProvider(connection.getVideoProvider());
    907         setVideoState(connection.getVideoState());
    908         setRingbackRequested(connection.isRingbackRequested());
    909         setIsVoipAudioMode(connection.getIsVoipAudioMode());
    910         setStatusHints(connection.getStatusHints());
    911         setExtras(connection.getExtras());
    912 
    913         mConferenceableCalls.clear();
    914         for (String id : connection.getConferenceableConnectionIds()) {
    915             mConferenceableCalls.add(idMapper.getCall(id));
    916         }
    917 
    918         if (mIsUnknown) {
    919             for (Listener l : mListeners) {
    920                 l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection.getState()));
    921             }
    922         } else if (mIsIncoming) {
    923             // We do not handle incoming calls immediately when they are verified by the connection
    924             // service. We allow the caller-info-query code to execute first so that we can read the
    925             // direct-to-voicemail property before deciding if we want to show the incoming call to
    926             // the user or if we want to reject the call.
    927             mDirectToVoicemailQueryPending = true;
    928 
    929             // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
    930             // showing the user the incoming call screen.
    931             mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis(
    932                     mContext.getContentResolver()));
    933         } else {
    934             for (Listener l : mListeners) {
    935                 l.onSuccessfulOutgoingCall(this,
    936                         getStateFromConnectionState(connection.getState()));
    937             }
    938         }
    939     }
    940 
    941     @Override
    942     public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
    943         clearConnectionService();
    944         setDisconnectCause(disconnectCause);
    945         mCallsManager.markCallAsDisconnected(this, disconnectCause);
    946 
    947         if (mIsUnknown) {
    948             for (Listener listener : mListeners) {
    949                 listener.onFailedUnknownCall(this);
    950             }
    951         } else if (mIsIncoming) {
    952             for (Listener listener : mListeners) {
    953                 listener.onFailedIncomingCall(this);
    954             }
    955         } else {
    956             for (Listener listener : mListeners) {
    957                 listener.onFailedOutgoingCall(this, disconnectCause);
    958             }
    959         }
    960     }
    961 
    962     /**
    963      * Plays the specified DTMF tone.
    964      */
    965     void playDtmfTone(char digit) {
    966         if (mConnectionService == null) {
    967             Log.w(this, "playDtmfTone() request on a call without a connection service.");
    968         } else {
    969             Log.i(this, "Send playDtmfTone to connection service for call %s", this);
    970             mConnectionService.playDtmfTone(this, digit);
    971             Log.event(this, Log.Events.START_DTMF, Log.pii(digit));
    972         }
    973     }
    974 
    975     /**
    976      * Stops playing any currently playing DTMF tone.
    977      */
    978     void stopDtmfTone() {
    979         if (mConnectionService == null) {
    980             Log.w(this, "stopDtmfTone() request on a call without a connection service.");
    981         } else {
    982             Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
    983             Log.event(this, Log.Events.STOP_DTMF);
    984             mConnectionService.stopDtmfTone(this);
    985         }
    986     }
    987 
    988     void disconnect() {
    989         disconnect(false);
    990     }
    991 
    992     /**
    993      * Attempts to disconnect the call through the connection service.
    994      */
    995     void disconnect(boolean wasViaNewOutgoingCallBroadcaster) {
    996         Log.event(this, Log.Events.REQUEST_DISCONNECT);
    997 
    998         // Track that the call is now locally disconnecting.
    999         setLocallyDisconnecting(true);
   1000 
   1001         if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||
   1002                 mState == CallState.CONNECTING) {
   1003             Log.v(this, "Aborting call %s", this);
   1004             abort(wasViaNewOutgoingCallBroadcaster);
   1005         } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
   1006             if (mConnectionService == null) {
   1007                 Log.e(this, new Exception(), "disconnect() request on a call without a"
   1008                         + " connection service.");
   1009             } else {
   1010                 Log.i(this, "Send disconnect to connection service for call: %s", this);
   1011                 // The call isn't officially disconnected until the connection service
   1012                 // confirms that the call was actually disconnected. Only then is the
   1013                 // association between call and connection service severed, see
   1014                 // {@link CallsManager#markCallAsDisconnected}.
   1015                 mConnectionService.disconnect(this);
   1016             }
   1017         }
   1018     }
   1019 
   1020     void abort(boolean wasViaNewOutgoingCallBroadcaster) {
   1021         if (mCreateConnectionProcessor != null &&
   1022                 !mCreateConnectionProcessor.isProcessingComplete()) {
   1023             mCreateConnectionProcessor.abort();
   1024         } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT
   1025                 || mState == CallState.CONNECTING) {
   1026             if (wasViaNewOutgoingCallBroadcaster) {
   1027                 // If the cancelation was from NEW_OUTGOING_CALL, then we do not automatically
   1028                 // destroy the call.  Instead, we announce the cancelation and CallsManager handles
   1029                 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and
   1030                 // then re-dial them quickly using a gateway, allowing the first call to end
   1031                 // causes jank. This timeout allows CallsManager to transition the first call into
   1032                 // the second call so that in-call only ever sees a single call...eliminating the
   1033                 // jank altogether.
   1034                 for (Listener listener : mListeners) {
   1035                     if (listener.onCanceledViaNewOutgoingCallBroadcast(this)) {
   1036                         // The first listener to handle this wins. A return value of true means that
   1037                         // the listener will handle the disconnection process later and so we
   1038                         // should not continue it here.
   1039                         setLocallyDisconnecting(false);
   1040                         return;
   1041                     }
   1042                 }
   1043             }
   1044 
   1045             handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
   1046         } else {
   1047             Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING");
   1048         }
   1049     }
   1050 
   1051     /**
   1052      * Answers the call if it is ringing.
   1053      *
   1054      * @param videoState The video state in which to answer the call.
   1055      */
   1056     void answer(int videoState) {
   1057         Preconditions.checkNotNull(mConnectionService);
   1058 
   1059         // Check to verify that the call is still in the ringing state. A call can change states
   1060         // between the time the user hits 'answer' and Telecom receives the command.
   1061         if (isRinging("answer")) {
   1062             // At this point, we are asking the connection service to answer but we don't assume
   1063             // that it will work. Instead, we wait until confirmation from the connectino service
   1064             // that the call is in a non-STATE_RINGING state before changing the UI. See
   1065             // {@link ConnectionServiceAdapter#setActive} and other set* methods.
   1066             mConnectionService.answer(this, videoState);
   1067             Log.event(this, Log.Events.REQUEST_ACCEPT);
   1068         }
   1069     }
   1070 
   1071     /**
   1072      * Rejects the call if it is ringing.
   1073      *
   1074      * @param rejectWithMessage Whether to send a text message as part of the call rejection.
   1075      * @param textMessage An optional text message to send as part of the rejection.
   1076      */
   1077     void reject(boolean rejectWithMessage, String textMessage) {
   1078         Preconditions.checkNotNull(mConnectionService);
   1079 
   1080         // Check to verify that the call is still in the ringing state. A call can change states
   1081         // between the time the user hits 'reject' and Telecomm receives the command.
   1082         if (isRinging("reject")) {
   1083             // Ensure video state history tracks video state at time of rejection.
   1084             mVideoStateHistory |= mVideoState;
   1085 
   1086             mConnectionService.reject(this);
   1087             Log.event(this, Log.Events.REQUEST_REJECT);
   1088         }
   1089     }
   1090 
   1091     /**
   1092      * Puts the call on hold if it is currently active.
   1093      */
   1094     void hold() {
   1095         Preconditions.checkNotNull(mConnectionService);
   1096 
   1097         if (mState == CallState.ACTIVE) {
   1098             mConnectionService.hold(this);
   1099             Log.event(this, Log.Events.REQUEST_HOLD);
   1100         }
   1101     }
   1102 
   1103     /**
   1104      * Releases the call from hold if it is currently active.
   1105      */
   1106     void unhold() {
   1107         Preconditions.checkNotNull(mConnectionService);
   1108 
   1109         if (mState == CallState.ON_HOLD) {
   1110             mConnectionService.unhold(this);
   1111             Log.event(this, Log.Events.REQUEST_UNHOLD);
   1112         }
   1113     }
   1114 
   1115     /** Checks if this is a live call or not. */
   1116     boolean isAlive() {
   1117         switch (mState) {
   1118             case CallState.NEW:
   1119             case CallState.RINGING:
   1120             case CallState.DISCONNECTED:
   1121             case CallState.ABORTED:
   1122                 return false;
   1123             default:
   1124                 return true;
   1125         }
   1126     }
   1127 
   1128     boolean isActive() {
   1129         return mState == CallState.ACTIVE;
   1130     }
   1131 
   1132     Bundle getExtras() {
   1133         return mExtras;
   1134     }
   1135 
   1136     void setExtras(Bundle extras) {
   1137         mExtras = extras;
   1138         for (Listener l : mListeners) {
   1139             l.onExtrasChanged(this);
   1140         }
   1141     }
   1142 
   1143     Bundle getIntentExtras() {
   1144         return mIntentExtras;
   1145     }
   1146 
   1147     void setIntentExtras(Bundle extras) {
   1148         mIntentExtras = extras;
   1149     }
   1150 
   1151     /**
   1152      * @return the uri of the contact associated with this call.
   1153      */
   1154     Uri getContactUri() {
   1155         if (mCallerInfo == null || !mCallerInfo.contactExists) {
   1156             return getHandle();
   1157         }
   1158         return Contacts.getLookupUri(mCallerInfo.contactIdOrZero, mCallerInfo.lookupKey);
   1159     }
   1160 
   1161     Uri getRingtone() {
   1162         return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
   1163     }
   1164 
   1165     void onPostDialWait(String remaining) {
   1166         for (Listener l : mListeners) {
   1167             l.onPostDialWait(this, remaining);
   1168         }
   1169     }
   1170 
   1171     void onPostDialChar(char nextChar) {
   1172         for (Listener l : mListeners) {
   1173             l.onPostDialChar(this, nextChar);
   1174         }
   1175     }
   1176 
   1177     void postDialContinue(boolean proceed) {
   1178         mConnectionService.onPostDialContinue(this, proceed);
   1179     }
   1180 
   1181     void conferenceWith(Call otherCall) {
   1182         if (mConnectionService == null) {
   1183             Log.w(this, "conference requested on a call without a connection service.");
   1184         } else {
   1185             Log.event(this, Log.Events.CONFERENCE_WITH, otherCall);
   1186             mConnectionService.conference(this, otherCall);
   1187         }
   1188     }
   1189 
   1190     void splitFromConference() {
   1191         if (mConnectionService == null) {
   1192             Log.w(this, "splitting from conference call without a connection service");
   1193         } else {
   1194             Log.event(this, Log.Events.SPLIT_CONFERENCE);
   1195             mConnectionService.splitFromConference(this);
   1196         }
   1197     }
   1198 
   1199     void mergeConference() {
   1200         if (mConnectionService == null) {
   1201             Log.w(this, "merging conference calls without a connection service.");
   1202         } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
   1203             Log.event(this, Log.Events.CONFERENCE_WITH);
   1204             mConnectionService.mergeConference(this);
   1205             mWasConferencePreviouslyMerged = true;
   1206         }
   1207     }
   1208 
   1209     void swapConference() {
   1210         if (mConnectionService == null) {
   1211             Log.w(this, "swapping conference calls without a connection service.");
   1212         } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
   1213             Log.event(this, Log.Events.SWAP);
   1214             mConnectionService.swapConference(this);
   1215             switch (mChildCalls.size()) {
   1216                 case 1:
   1217                     mConferenceLevelActiveCall = mChildCalls.get(0);
   1218                     break;
   1219                 case 2:
   1220                     // swap
   1221                     mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ?
   1222                             mChildCalls.get(1) : mChildCalls.get(0);
   1223                     break;
   1224                 default:
   1225                     // For anything else 0, or 3+, set it to null since it is impossible to tell.
   1226                     mConferenceLevelActiveCall = null;
   1227                     break;
   1228             }
   1229         }
   1230     }
   1231 
   1232     void setParentCall(Call parentCall) {
   1233         if (parentCall == this) {
   1234             Log.e(this, new Exception(), "setting the parent to self");
   1235             return;
   1236         }
   1237         if (parentCall == mParentCall) {
   1238             // nothing to do
   1239             return;
   1240         }
   1241         Preconditions.checkState(parentCall == null || mParentCall == null);
   1242 
   1243         Call oldParent = mParentCall;
   1244         if (mParentCall != null) {
   1245             mParentCall.removeChildCall(this);
   1246         }
   1247         mParentCall = parentCall;
   1248         if (mParentCall != null) {
   1249             mParentCall.addChildCall(this);
   1250         }
   1251 
   1252         Log.event(this, Log.Events.SET_PARENT, mParentCall);
   1253         for (Listener l : mListeners) {
   1254             l.onParentChanged(this);
   1255         }
   1256     }
   1257 
   1258     void setConferenceableCalls(List<Call> conferenceableCalls) {
   1259         mConferenceableCalls.clear();
   1260         mConferenceableCalls.addAll(conferenceableCalls);
   1261 
   1262         for (Listener l : mListeners) {
   1263             l.onConferenceableCallsChanged(this);
   1264         }
   1265     }
   1266 
   1267     List<Call> getConferenceableCalls() {
   1268         return mConferenceableCalls;
   1269     }
   1270 
   1271     boolean can(int capability) {
   1272         return (mConnectionCapabilities & capability) == capability;
   1273     }
   1274 
   1275     private void addChildCall(Call call) {
   1276         if (!mChildCalls.contains(call)) {
   1277             // Set the pseudo-active call to the latest child added to the conference.
   1278             // See definition of mConferenceLevelActiveCall for more detail.
   1279             mConferenceLevelActiveCall = call;
   1280             mChildCalls.add(call);
   1281 
   1282             Log.event(this, Log.Events.ADD_CHILD, call);
   1283 
   1284             for (Listener l : mListeners) {
   1285                 l.onChildrenChanged(this);
   1286             }
   1287         }
   1288     }
   1289 
   1290     private void removeChildCall(Call call) {
   1291         if (mChildCalls.remove(call)) {
   1292             Log.event(this, Log.Events.REMOVE_CHILD, call);
   1293             for (Listener l : mListeners) {
   1294                 l.onChildrenChanged(this);
   1295             }
   1296         }
   1297     }
   1298 
   1299     /**
   1300      * Return whether the user can respond to this {@code Call} via an SMS message.
   1301      *
   1302      * @return true if the "Respond via SMS" feature should be enabled
   1303      * for this incoming call.
   1304      *
   1305      * The general rule is that we *do* allow "Respond via SMS" except for
   1306      * the few (relatively rare) cases where we know for sure it won't
   1307      * work, namely:
   1308      *   - a bogus or blank incoming number
   1309      *   - a call from a SIP address
   1310      *   - a "call presentation" that doesn't allow the number to be revealed
   1311      *
   1312      * In all other cases, we allow the user to respond via SMS.
   1313      *
   1314      * Note that this behavior isn't perfect; for example we have no way
   1315      * to detect whether the incoming call is from a landline (with most
   1316      * networks at least), so we still enable this feature even though
   1317      * SMSes to that number will silently fail.
   1318      */
   1319     boolean isRespondViaSmsCapable() {
   1320         if (mState != CallState.RINGING) {
   1321             return false;
   1322         }
   1323 
   1324         if (getHandle() == null) {
   1325             // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
   1326             // other words, the user should not be able to see the incoming phone number.
   1327             return false;
   1328         }
   1329 
   1330         if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
   1331             // The incoming number is actually a URI (i.e. a SIP address),
   1332             // not a regular PSTN phone number, and we can't send SMSes to
   1333             // SIP addresses.
   1334             // (TODO: That might still be possible eventually, though. Is
   1335             // there some SIP-specific equivalent to sending a text message?)
   1336             return false;
   1337         }
   1338 
   1339         // Is there a valid SMS application on the phone?
   1340         if (SmsApplication.getDefaultRespondViaMessageApplication(mContext,
   1341                 true /*updateIfNeeded*/) == null) {
   1342             return false;
   1343         }
   1344 
   1345         // TODO: with some carriers (in certain countries) you *can* actually
   1346         // tell whether a given number is a mobile phone or not. So in that
   1347         // case we could potentially return false here if the incoming call is
   1348         // from a land line.
   1349 
   1350         // If none of the above special cases apply, it's OK to enable the
   1351         // "Respond via SMS" feature.
   1352         return true;
   1353     }
   1354 
   1355     List<String> getCannedSmsResponses() {
   1356         return mCannedSmsResponses;
   1357     }
   1358 
   1359     /**
   1360      * We need to make sure that before we move a call to the disconnected state, it no
   1361      * longer has any parent/child relationships.  We want to do this to ensure that the InCall
   1362      * Service always has the right data in the right order.  We also want to do it in telecom so
   1363      * that the insurance policy lives in the framework side of things.
   1364      */
   1365     private void fixParentAfterDisconnect() {
   1366         setParentCall(null);
   1367     }
   1368 
   1369     /**
   1370      * @return True if the call is ringing, else logs the action name.
   1371      */
   1372     private boolean isRinging(String actionName) {
   1373         if (mState == CallState.RINGING) {
   1374             return true;
   1375         }
   1376 
   1377         Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
   1378         return false;
   1379     }
   1380 
   1381     @SuppressWarnings("rawtypes")
   1382     private void decrementAssociatedCallCount(ServiceBinder binder) {
   1383         if (binder != null) {
   1384             binder.decrementAssociatedCallCount();
   1385         }
   1386     }
   1387 
   1388     /**
   1389      * Looks up contact information based on the current handle.
   1390      */
   1391     private void startCallerInfoLookup() {
   1392         final String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
   1393 
   1394         mQueryToken++;  // Updated so that previous queries can no longer set the information.
   1395         mCallerInfo = null;
   1396         if (!TextUtils.isEmpty(number)) {
   1397             Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
   1398             mHandler.post(new Runnable() {
   1399                 @Override
   1400                 public void run() {
   1401                     mCallerInfoAsyncQueryFactory.startQuery(
   1402                             mQueryToken,
   1403                             mContext,
   1404                             number,
   1405                             mCallerInfoQueryListener,
   1406                             Call.this);
   1407                 }
   1408             });
   1409         }
   1410     }
   1411 
   1412     /**
   1413      * Saves the specified caller info if the specified token matches that of the last query
   1414      * that was made.
   1415      *
   1416      * @param callerInfo The new caller information to set.
   1417      * @param token The token used with this query.
   1418      */
   1419     private void setCallerInfo(CallerInfo callerInfo, int token) {
   1420         Trace.beginSection("setCallerInfo");
   1421         Preconditions.checkNotNull(callerInfo);
   1422 
   1423         if (mQueryToken == token) {
   1424             mCallerInfo = callerInfo;
   1425             Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
   1426 
   1427             if (mCallerInfo.contactDisplayPhotoUri != null) {
   1428                 Log.d(this, "Searching person uri %s for call %s",
   1429                         mCallerInfo.contactDisplayPhotoUri, this);
   1430                 mContactsAsyncHelper.startObtainPhotoAsync(
   1431                         token,
   1432                         mContext,
   1433                         mCallerInfo.contactDisplayPhotoUri,
   1434                         mPhotoLoadListener,
   1435                         this);
   1436                 // Do not call onCallerInfoChanged yet in this case.  We call it in setPhoto().
   1437             } else {
   1438                 for (Listener l : mListeners) {
   1439                     l.onCallerInfoChanged(this);
   1440                 }
   1441             }
   1442 
   1443             processDirectToVoicemail();
   1444         }
   1445         Trace.endSection();
   1446     }
   1447 
   1448     CallerInfo getCallerInfo() {
   1449         return mCallerInfo;
   1450     }
   1451 
   1452     /**
   1453      * Saves the specified photo information if the specified token matches that of the last query.
   1454      *
   1455      * @param photo The photo as a drawable.
   1456      * @param photoIcon The photo as a small icon.
   1457      * @param token The token used with this query.
   1458      */
   1459     private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
   1460         if (mQueryToken == token) {
   1461             mCallerInfo.cachedPhoto = photo;
   1462             mCallerInfo.cachedPhotoIcon = photoIcon;
   1463 
   1464             for (Listener l : mListeners) {
   1465                 l.onCallerInfoChanged(this);
   1466             }
   1467         }
   1468     }
   1469 
   1470     private void maybeLoadCannedSmsResponses() {
   1471         if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
   1472             Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
   1473             mCannedSmsResponsesLoadingStarted = true;
   1474             mCallsManager.getRespondViaSmsManager().loadCannedTextMessages(
   1475                     new Response<Void, List<String>>() {
   1476                         @Override
   1477                         public void onResult(Void request, List<String>... result) {
   1478                             if (result.length > 0) {
   1479                                 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
   1480                                 mCannedSmsResponses = result[0];
   1481                                 for (Listener l : mListeners) {
   1482                                     l.onCannedSmsResponsesLoaded(Call.this);
   1483                                 }
   1484                             }
   1485                         }
   1486 
   1487                         @Override
   1488                         public void onError(Void request, int code, String msg) {
   1489                             Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
   1490                                     msg);
   1491                         }
   1492                     },
   1493                     mContext
   1494             );
   1495         } else {
   1496             Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
   1497         }
   1498     }
   1499 
   1500     /**
   1501      * Sets speakerphone option on when call begins.
   1502      */
   1503     public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
   1504         mSpeakerphoneOn = startWithSpeakerphone;
   1505     }
   1506 
   1507     /**
   1508      * Returns speakerphone option.
   1509      *
   1510      * @return Whether or not speakerphone should be set automatically when call begins.
   1511      */
   1512     public boolean getStartWithSpeakerphoneOn() {
   1513         return mSpeakerphoneOn;
   1514     }
   1515 
   1516     /**
   1517      * Sets a video call provider for the call.
   1518      */
   1519     public void setVideoProvider(IVideoProvider videoProvider) {
   1520         Log.v(this, "setVideoProvider");
   1521 
   1522         if (videoProvider != null ) {
   1523             try {
   1524                 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this);
   1525             } catch (RemoteException ignored) {
   1526                 // Ignore RemoteException.
   1527             }
   1528         } else {
   1529             mVideoProviderProxy = null;
   1530         }
   1531 
   1532         mVideoProvider = videoProvider;
   1533 
   1534         for (Listener l : mListeners) {
   1535             l.onVideoCallProviderChanged(Call.this);
   1536         }
   1537     }
   1538 
   1539     /**
   1540      * @return The {@link Connection.VideoProvider} binder.
   1541      */
   1542     public IVideoProvider getVideoProvider() {
   1543         if (mVideoProviderProxy == null) {
   1544             return null;
   1545         }
   1546 
   1547         return mVideoProviderProxy.getInterface();
   1548     }
   1549 
   1550     /**
   1551      * @return The {@link VideoProviderProxy} for this call.
   1552      */
   1553     public VideoProviderProxy getVideoProviderProxy() {
   1554         return mVideoProviderProxy;
   1555     }
   1556 
   1557     /**
   1558      * The current video state for the call.
   1559      * See {@link VideoProfile} for a list of valid video states.
   1560      */
   1561     public int getVideoState() {
   1562         return mVideoState;
   1563     }
   1564 
   1565     /**
   1566      * Returns the video states which were applicable over the duration of a call.
   1567      * See {@link VideoProfile} for a list of valid video states.
   1568      *
   1569      * @return The video states applicable over the duration of the call.
   1570      */
   1571     public int getVideoStateHistory() {
   1572         return mVideoStateHistory;
   1573     }
   1574 
   1575     /**
   1576      * Determines the current video state for the call.
   1577      * For an outgoing call determines the desired video state for the call.
   1578      * Valid values: see {@link VideoProfile}
   1579      *
   1580      * @param videoState The video state for the call.
   1581      */
   1582     public void setVideoState(int videoState) {
   1583         // Track which video states were applicable over the duration of the call.
   1584         // Only track the call state when the call is active or disconnected.  This ensures we do
   1585         // not include the video state when:
   1586         // - Call is incoming (but not answered).
   1587         // - Call it outgoing (but not answered).
   1588         // We include the video state when disconnected to ensure that rejected calls reflect the
   1589         // appropriate video state.
   1590         if (isActive() || getState() == CallState.DISCONNECTED) {
   1591             mVideoStateHistory = mVideoStateHistory | videoState;
   1592         }
   1593 
   1594         mVideoState = videoState;
   1595         for (Listener l : mListeners) {
   1596             l.onVideoStateChanged(this);
   1597         }
   1598     }
   1599 
   1600     public boolean getIsVoipAudioMode() {
   1601         return mIsVoipAudioMode;
   1602     }
   1603 
   1604     public void setIsVoipAudioMode(boolean audioModeIsVoip) {
   1605         mIsVoipAudioMode = audioModeIsVoip;
   1606         for (Listener l : mListeners) {
   1607             l.onIsVoipAudioModeChanged(this);
   1608         }
   1609     }
   1610 
   1611     public StatusHints getStatusHints() {
   1612         return mStatusHints;
   1613     }
   1614 
   1615     public void setStatusHints(StatusHints statusHints) {
   1616         mStatusHints = statusHints;
   1617         for (Listener l : mListeners) {
   1618             l.onStatusHintsChanged(this);
   1619         }
   1620     }
   1621 
   1622     public boolean isUnknown() {
   1623         return mIsUnknown;
   1624     }
   1625 
   1626     public void setIsUnknown(boolean isUnknown) {
   1627         mIsUnknown = isUnknown;
   1628     }
   1629 
   1630     /**
   1631      * Determines if this call is in a disconnecting state.
   1632      *
   1633      * @return {@code true} if this call is locally disconnecting.
   1634      */
   1635     public boolean isLocallyDisconnecting() {
   1636         return mIsLocallyDisconnecting;
   1637     }
   1638 
   1639     /**
   1640      * Sets whether this call is in a disconnecting state.
   1641      *
   1642      * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting.
   1643      */
   1644     private void setLocallyDisconnecting(boolean isLocallyDisconnecting) {
   1645         mIsLocallyDisconnecting = isLocallyDisconnecting;
   1646     }
   1647 
   1648     static int getStateFromConnectionState(int state) {
   1649         switch (state) {
   1650             case Connection.STATE_INITIALIZING:
   1651                 return CallState.CONNECTING;
   1652             case Connection.STATE_ACTIVE:
   1653                 return CallState.ACTIVE;
   1654             case Connection.STATE_DIALING:
   1655                 return CallState.DIALING;
   1656             case Connection.STATE_DISCONNECTED:
   1657                 return CallState.DISCONNECTED;
   1658             case Connection.STATE_HOLDING:
   1659                 return CallState.ON_HOLD;
   1660             case Connection.STATE_NEW:
   1661                 return CallState.NEW;
   1662             case Connection.STATE_RINGING:
   1663                 return CallState.RINGING;
   1664         }
   1665         return CallState.DISCONNECTED;
   1666     }
   1667 }
   1668