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.provider.ContactsContract.Contacts;
     26 import android.telecom.CallState;
     27 import android.telecom.DisconnectCause;
     28 import android.telecom.Connection;
     29 import android.telecom.GatewayInfo;
     30 import android.telecom.ParcelableConnection;
     31 import android.telecom.PhoneAccount;
     32 import android.telecom.PhoneAccountHandle;
     33 import android.telecom.PhoneCapabilities;
     34 import android.telecom.Response;
     35 import android.telecom.StatusHints;
     36 import android.telecom.TelecomManager;
     37 import android.telecom.VideoProfile;
     38 import android.telephony.PhoneNumberUtils;
     39 import android.text.TextUtils;
     40 
     41 import com.android.internal.telecom.IVideoProvider;
     42 import com.android.internal.telephony.CallerInfo;
     43 import com.android.internal.telephony.CallerInfoAsyncQuery;
     44 import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
     45 import com.android.internal.telephony.SmsApplication;
     46 import com.android.server.telecom.ContactsAsyncHelper.OnImageLoadCompleteListener;
     47 
     48 import com.android.internal.util.Preconditions;
     49 
     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 final class Call implements CreateConnectionResponse {
     65     /**
     66      * Listener for events on the call.
     67      */
     68     interface Listener {
     69         void onSuccessfulOutgoingCall(Call call, int callState);
     70         void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause);
     71         void onSuccessfulIncomingCall(Call call);
     72         void onFailedIncomingCall(Call call);
     73         void onSuccessfulUnknownCall(Call call, int callState);
     74         void onFailedUnknownCall(Call call);
     75         void onRingbackRequested(Call call, boolean ringbackRequested);
     76         void onPostDialWait(Call call, String remaining);
     77         void onCallCapabilitiesChanged(Call call);
     78         void onParentChanged(Call call);
     79         void onChildrenChanged(Call call);
     80         void onCannedSmsResponsesLoaded(Call call);
     81         void onVideoCallProviderChanged(Call call);
     82         void onCallerInfoChanged(Call call);
     83         void onIsVoipAudioModeChanged(Call call);
     84         void onStatusHintsChanged(Call call);
     85         void onHandleChanged(Call call);
     86         void onCallerDisplayNameChanged(Call call);
     87         void onVideoStateChanged(Call call);
     88         void onTargetPhoneAccountChanged(Call call);
     89         void onConnectionManagerPhoneAccountChanged(Call call);
     90         void onPhoneAccountChanged(Call call);
     91         void onConferenceableCallsChanged(Call call);
     92     }
     93 
     94     abstract static class ListenerBase implements Listener {
     95         @Override
     96         public void onSuccessfulOutgoingCall(Call call, int callState) {}
     97         @Override
     98         public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {}
     99         @Override
    100         public void onSuccessfulIncomingCall(Call call) {}
    101         @Override
    102         public void onFailedIncomingCall(Call call) {}
    103         @Override
    104         public void onSuccessfulUnknownCall(Call call, int callState) {}
    105         @Override
    106         public void onFailedUnknownCall(Call call) {}
    107         @Override
    108         public void onRingbackRequested(Call call, boolean ringbackRequested) {}
    109         @Override
    110         public void onPostDialWait(Call call, String remaining) {}
    111         @Override
    112         public void onCallCapabilitiesChanged(Call call) {}
    113         @Override
    114         public void onParentChanged(Call call) {}
    115         @Override
    116         public void onChildrenChanged(Call call) {}
    117         @Override
    118         public void onCannedSmsResponsesLoaded(Call call) {}
    119         @Override
    120         public void onVideoCallProviderChanged(Call call) {}
    121         @Override
    122         public void onCallerInfoChanged(Call call) {}
    123         @Override
    124         public void onIsVoipAudioModeChanged(Call call) {}
    125         @Override
    126         public void onStatusHintsChanged(Call call) {}
    127         @Override
    128         public void onHandleChanged(Call call) {}
    129         @Override
    130         public void onCallerDisplayNameChanged(Call call) {}
    131         @Override
    132         public void onVideoStateChanged(Call call) {}
    133         @Override
    134         public void onTargetPhoneAccountChanged(Call call) {}
    135         @Override
    136         public void onConnectionManagerPhoneAccountChanged(Call call) {}
    137         @Override
    138         public void onPhoneAccountChanged(Call call) {}
    139         @Override
    140         public void onConferenceableCallsChanged(Call call) {}
    141     }
    142 
    143     private static final OnQueryCompleteListener sCallerInfoQueryListener =
    144             new OnQueryCompleteListener() {
    145                 /** ${inheritDoc} */
    146                 @Override
    147                 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
    148                     if (cookie != null) {
    149                         ((Call) cookie).setCallerInfo(callerInfo, token);
    150                     }
    151                 }
    152             };
    153 
    154     private static final OnImageLoadCompleteListener sPhotoLoadListener =
    155             new OnImageLoadCompleteListener() {
    156                 /** ${inheritDoc} */
    157                 @Override
    158                 public void onImageLoadComplete(
    159                         int token, Drawable photo, Bitmap photoIcon, Object cookie) {
    160                     if (cookie != null) {
    161                         ((Call) cookie).setPhoto(photo, photoIcon, token);
    162                     }
    163                 }
    164             };
    165 
    166     private final Runnable mDirectToVoicemailRunnable = new Runnable() {
    167         @Override
    168         public void run() {
    169             processDirectToVoicemail();
    170         }
    171     };
    172 
    173     /** True if this is an incoming call. */
    174     private final boolean mIsIncoming;
    175 
    176     /** True if this is a currently unknown call that was not previously tracked by CallsManager,
    177      *  and did not originate via the regular incoming/outgoing call code paths.
    178      */
    179     private boolean mIsUnknown;
    180 
    181     /**
    182      * The time this call was created. Beyond logging and such, may also be used for bookkeeping
    183      * and specifically for marking certain call attempts as failed attempts.
    184      */
    185     private final long mCreationTimeMillis = System.currentTimeMillis();
    186 
    187     /** The gateway information associated with this call. This stores the original call handle
    188      * that the user is attempting to connect to via the gateway, the actual handle to dial in
    189      * order to connect the call via the gateway, as well as the package name of the gateway
    190      * service. */
    191     private GatewayInfo mGatewayInfo;
    192 
    193     private PhoneAccountHandle mConnectionManagerPhoneAccountHandle;
    194 
    195     private PhoneAccountHandle mTargetPhoneAccountHandle;
    196 
    197     private final Handler mHandler = new Handler();
    198 
    199     private final List<Call> mConferenceableCalls = new ArrayList<>();
    200 
    201     private long mConnectTimeMillis = 0;
    202 
    203     /** The state of the call. */
    204     private int mState;
    205 
    206     /** The handle with which to establish this call. */
    207     private Uri mHandle;
    208 
    209     /**
    210      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
    211      */
    212     private int mHandlePresentation;
    213 
    214     /** The caller display name (CNAP) set by the connection service. */
    215     private String mCallerDisplayName;
    216 
    217     /**
    218      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
    219      */
    220     private int mCallerDisplayNamePresentation;
    221 
    222     /**
    223      * The connection service which is attempted or already connecting this call.
    224      */
    225     private ConnectionServiceWrapper mConnectionService;
    226 
    227     private boolean mIsEmergencyCall;
    228 
    229     private boolean mSpeakerphoneOn;
    230 
    231     /**
    232      * Tracks the video states which were applicable over the duration of a call.
    233      * See {@link VideoProfile} for a list of valid video states.
    234      */
    235     private int mVideoStateHistory;
    236 
    237     private int mVideoState;
    238 
    239     /**
    240      * Disconnect cause for the call. Only valid if the state of the call is STATE_DISCONNECTED.
    241      * See {@link android.telecom.DisconnectCause}.
    242      */
    243     private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);
    244 
    245     /** Info used by the connection services. */
    246     private Bundle mExtras = Bundle.EMPTY;
    247 
    248     /** Set of listeners on this call.
    249      *
    250      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
    251      * load factor before resizing, 1 means we only expect a single thread to
    252      * access the map so make only a single shard
    253      */
    254     private final Set<Listener> mListeners = Collections.newSetFromMap(
    255             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
    256 
    257     private CreateConnectionProcessor mCreateConnectionProcessor;
    258 
    259     /** Caller information retrieved from the latest contact query. */
    260     private CallerInfo mCallerInfo;
    261 
    262     /** The latest token used with a contact info query. */
    263     private int mQueryToken = 0;
    264 
    265     /** Whether this call is requesting that Telecom play the ringback tone on its behalf. */
    266     private boolean mRingbackRequested = false;
    267 
    268     /** Whether direct-to-voicemail query is pending. */
    269     private boolean mDirectToVoicemailQueryPending;
    270 
    271     private int mCallCapabilities;
    272 
    273     private boolean mIsConference = false;
    274 
    275     private Call mParentCall = null;
    276 
    277     private List<Call> mChildCalls = new LinkedList<>();
    278 
    279     /** Set of text message responses allowed for this call, if applicable. */
    280     private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
    281 
    282     /** Whether an attempt has been made to load the text message responses. */
    283     private boolean mCannedSmsResponsesLoadingStarted = false;
    284 
    285     private IVideoProvider mVideoProvider;
    286 
    287     private boolean mIsVoipAudioMode;
    288     private StatusHints mStatusHints;
    289     private final ConnectionServiceRepository mRepository;
    290     private final Context mContext;
    291 
    292     private boolean mWasConferencePreviouslyMerged = false;
    293 
    294     // For conferences which support merge/swap at their level, we retain a notion of an active call.
    295     // This is used for BluetoothPhoneService.  In order to support hold/merge, it must have the notion
    296     // of the current "active" call within the conference call. This maintains the "active" call and
    297     // switches every time the user hits "swap".
    298     private Call mConferenceLevelActiveCall = null;
    299 
    300     private boolean mIsLocallyDisconnecting = false;
    301 
    302     /**
    303      * Persists the specified parameters and initializes the new instance.
    304      *
    305      * @param context The context.
    306      * @param repository The connection service repository.
    307      * @param handle The handle to dial.
    308      * @param gatewayInfo Gateway information to use for the call.
    309      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
    310      *         This account must be one that was registered with the
    311      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
    312      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
    313      *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
    314      * @param isIncoming True if this is an incoming call.
    315      */
    316     Call(
    317             Context context,
    318             ConnectionServiceRepository repository,
    319             Uri handle,
    320             GatewayInfo gatewayInfo,
    321             PhoneAccountHandle connectionManagerPhoneAccountHandle,
    322             PhoneAccountHandle targetPhoneAccountHandle,
    323             boolean isIncoming,
    324             boolean isConference) {
    325         mState = isConference ? CallState.ACTIVE : CallState.NEW;
    326         mContext = context;
    327         mRepository = repository;
    328         setHandle(handle);
    329         setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
    330         mGatewayInfo = gatewayInfo;
    331         setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
    332         setTargetPhoneAccount(targetPhoneAccountHandle);
    333         mIsIncoming = isIncoming;
    334         mIsConference = isConference;
    335         maybeLoadCannedSmsResponses();
    336     }
    337 
    338     void addListener(Listener listener) {
    339         mListeners.add(listener);
    340     }
    341 
    342     void removeListener(Listener listener) {
    343         if (listener != null) {
    344             mListeners.remove(listener);
    345         }
    346     }
    347 
    348     /** {@inheritDoc} */
    349     @Override
    350     public String toString() {
    351         String component = null;
    352         if (mConnectionService != null && mConnectionService.getComponentName() != null) {
    353             component = mConnectionService.getComponentName().flattenToShortString();
    354         }
    355 
    356         return String.format(Locale.US, "[%s, %s, %s, %s, %d, childs(%d), has_parent(%b), [%s]",
    357                 System.identityHashCode(this),
    358                 CallState.toString(mState),
    359                 component,
    360                 Log.piiHandle(mHandle),
    361                 getVideoState(),
    362                 getChildCalls().size(),
    363                 getParentCall() != null,
    364                 PhoneCapabilities.toString(getCallCapabilities()));
    365     }
    366 
    367     int getState() {
    368         return mState;
    369     }
    370 
    371     /**
    372      * Sets the call state. Although there exists the notion of appropriate state transitions
    373      * (see {@link CallState}), in practice those expectations break down when cellular systems
    374      * misbehave and they do this very often. The result is that we do not enforce state transitions
    375      * and instead keep the code resilient to unexpected state changes.
    376      */
    377     void setState(int newState) {
    378         if (mState != newState) {
    379             Log.v(this, "setState %s -> %s", mState, newState);
    380             mState = newState;
    381             maybeLoadCannedSmsResponses();
    382 
    383             if (mState == CallState.DISCONNECTED) {
    384                 setLocallyDisconnecting(false);
    385                 fixParentAfterDisconnect();
    386             }
    387         }
    388     }
    389 
    390     void setRingbackRequested(boolean ringbackRequested) {
    391         mRingbackRequested = ringbackRequested;
    392         for (Listener l : mListeners) {
    393             l.onRingbackRequested(this, mRingbackRequested);
    394         }
    395     }
    396 
    397     boolean isRingbackRequested() {
    398         return mRingbackRequested;
    399     }
    400 
    401     boolean isConference() {
    402         return mIsConference;
    403     }
    404 
    405     Uri getHandle() {
    406         return mHandle;
    407     }
    408 
    409     int getHandlePresentation() {
    410         return mHandlePresentation;
    411     }
    412 
    413 
    414     void setHandle(Uri handle) {
    415         setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
    416     }
    417 
    418     void setHandle(Uri handle, int presentation) {
    419         if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
    420             mHandle = handle;
    421             mHandlePresentation = presentation;
    422             mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(mContext,
    423                     mHandle.getSchemeSpecificPart());
    424             startCallerInfoLookup();
    425             for (Listener l : mListeners) {
    426                 l.onHandleChanged(this);
    427             }
    428         }
    429     }
    430 
    431     String getCallerDisplayName() {
    432         return mCallerDisplayName;
    433     }
    434 
    435     int getCallerDisplayNamePresentation() {
    436         return mCallerDisplayNamePresentation;
    437     }
    438 
    439     void setCallerDisplayName(String callerDisplayName, int presentation) {
    440         if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
    441                 presentation != mCallerDisplayNamePresentation) {
    442             mCallerDisplayName = callerDisplayName;
    443             mCallerDisplayNamePresentation = presentation;
    444             for (Listener l : mListeners) {
    445                 l.onCallerDisplayNameChanged(this);
    446             }
    447         }
    448     }
    449 
    450     String getName() {
    451         return mCallerInfo == null ? null : mCallerInfo.name;
    452     }
    453 
    454     Bitmap getPhotoIcon() {
    455         return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
    456     }
    457 
    458     Drawable getPhoto() {
    459         return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
    460     }
    461 
    462     /**
    463      * @param disconnectCause The reason for the disconnection, represented by
    464      *         {@link android.telecom.DisconnectCause}.
    465      */
    466     void setDisconnectCause(DisconnectCause disconnectCause) {
    467         // TODO: Consider combining this method with a setDisconnected() method that is totally
    468         // separate from setState.
    469         mDisconnectCause = disconnectCause;
    470     }
    471 
    472     DisconnectCause getDisconnectCause() {
    473         return mDisconnectCause;
    474     }
    475 
    476     boolean isEmergencyCall() {
    477         return mIsEmergencyCall;
    478     }
    479 
    480     /**
    481      * @return The original handle this call is associated with. In-call services should use this
    482      * handle when indicating in their UI the handle that is being called.
    483      */
    484     public Uri getOriginalHandle() {
    485         if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
    486             return mGatewayInfo.getOriginalAddress();
    487         }
    488         return getHandle();
    489     }
    490 
    491     GatewayInfo getGatewayInfo() {
    492         return mGatewayInfo;
    493     }
    494 
    495     void setGatewayInfo(GatewayInfo gatewayInfo) {
    496         mGatewayInfo = gatewayInfo;
    497     }
    498 
    499     PhoneAccountHandle getConnectionManagerPhoneAccount() {
    500         return mConnectionManagerPhoneAccountHandle;
    501     }
    502 
    503     void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
    504         if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
    505             mConnectionManagerPhoneAccountHandle = accountHandle;
    506             for (Listener l : mListeners) {
    507                 l.onConnectionManagerPhoneAccountChanged(this);
    508             }
    509         }
    510 
    511     }
    512 
    513     PhoneAccountHandle getTargetPhoneAccount() {
    514         return mTargetPhoneAccountHandle;
    515     }
    516 
    517     void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
    518         if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
    519             mTargetPhoneAccountHandle = accountHandle;
    520             for (Listener l : mListeners) {
    521                 l.onTargetPhoneAccountChanged(this);
    522             }
    523         }
    524     }
    525 
    526     boolean isIncoming() {
    527         return mIsIncoming;
    528     }
    529 
    530     /**
    531      * @return The "age" of this call object in milliseconds, which typically also represents the
    532      *     period since this call was added to the set pending outgoing calls, see
    533      *     mCreationTimeMillis.
    534      */
    535     long getAgeMillis() {
    536         return System.currentTimeMillis() - mCreationTimeMillis;
    537     }
    538 
    539     /**
    540      * @return The time when this call object was created and added to the set of pending outgoing
    541      *     calls.
    542      */
    543     long getCreationTimeMillis() {
    544         return mCreationTimeMillis;
    545     }
    546 
    547     long getConnectTimeMillis() {
    548         return mConnectTimeMillis;
    549     }
    550 
    551     void setConnectTimeMillis(long connectTimeMillis) {
    552         mConnectTimeMillis = connectTimeMillis;
    553     }
    554 
    555     int getCallCapabilities() {
    556         return mCallCapabilities;
    557     }
    558 
    559     void setCallCapabilities(int callCapabilities) {
    560         setCallCapabilities(callCapabilities, false /* forceUpdate */);
    561     }
    562 
    563     void setCallCapabilities(int callCapabilities, boolean forceUpdate) {
    564         Log.v(this, "setCallCapabilities: %s", PhoneCapabilities.toString(callCapabilities));
    565         if (forceUpdate || mCallCapabilities != callCapabilities) {
    566            mCallCapabilities = callCapabilities;
    567             for (Listener l : mListeners) {
    568                 l.onCallCapabilitiesChanged(this);
    569             }
    570         }
    571     }
    572 
    573     Call getParentCall() {
    574         return mParentCall;
    575     }
    576 
    577     List<Call> getChildCalls() {
    578         return mChildCalls;
    579     }
    580 
    581     boolean wasConferencePreviouslyMerged() {
    582         return mWasConferencePreviouslyMerged;
    583     }
    584 
    585     Call getConferenceLevelActiveCall() {
    586         return mConferenceLevelActiveCall;
    587     }
    588 
    589     ConnectionServiceWrapper getConnectionService() {
    590         return mConnectionService;
    591     }
    592 
    593     /**
    594      * Retrieves the {@link Context} for the call.
    595      *
    596      * @return The {@link Context}.
    597      */
    598     Context getContext() {
    599         return mContext;
    600     }
    601 
    602     void setConnectionService(ConnectionServiceWrapper service) {
    603         Preconditions.checkNotNull(service);
    604 
    605         clearConnectionService();
    606 
    607         service.incrementAssociatedCallCount();
    608         mConnectionService = service;
    609         mConnectionService.addCall(this);
    610     }
    611 
    612     /**
    613      * Clears the associated connection service.
    614      */
    615     void clearConnectionService() {
    616         if (mConnectionService != null) {
    617             ConnectionServiceWrapper serviceTemp = mConnectionService;
    618             mConnectionService = null;
    619             serviceTemp.removeCall(this);
    620 
    621             // Decrementing the count can cause the service to unbind, which itself can trigger the
    622             // service-death code.  Since the service death code tries to clean up any associated
    623             // calls, we need to make sure to remove that information (e.g., removeCall()) before
    624             // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
    625             // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
    626             // to do.
    627             decrementAssociatedCallCount(serviceTemp);
    628         }
    629     }
    630 
    631     private void processDirectToVoicemail() {
    632         if (mDirectToVoicemailQueryPending) {
    633             if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
    634                 Log.i(this, "Directing call to voicemail: %s.", this);
    635                 // TODO: Once we move State handling from CallsManager to Call, we
    636                 // will not need to set STATE_RINGING state prior to calling reject.
    637                 setState(CallState.RINGING);
    638                 reject(false, null);
    639             } else {
    640                 // TODO: Make this class (not CallsManager) responsible for changing
    641                 // the call state to STATE_RINGING.
    642 
    643                 // TODO: Replace this with state transition to STATE_RINGING.
    644                 for (Listener l : mListeners) {
    645                     l.onSuccessfulIncomingCall(this);
    646                 }
    647             }
    648 
    649             mDirectToVoicemailQueryPending = false;
    650         }
    651     }
    652 
    653     /**
    654      * Starts the create connection sequence. Upon completion, there should exist an active
    655      * connection through a connection service (or the call will have failed).
    656      *
    657      * @param phoneAccountRegistrar The phone account registrar.
    658      */
    659     void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
    660         Preconditions.checkState(mCreateConnectionProcessor == null);
    661         mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
    662                 phoneAccountRegistrar, mContext);
    663         mCreateConnectionProcessor.process();
    664     }
    665 
    666     @Override
    667     public void handleCreateConnectionSuccess(
    668             CallIdMapper idMapper,
    669             ParcelableConnection connection) {
    670         Log.v(this, "handleCreateConnectionSuccessful %s", connection);
    671         mCreateConnectionProcessor = null;
    672         setTargetPhoneAccount(connection.getPhoneAccount());
    673         setHandle(connection.getHandle(), connection.getHandlePresentation());
    674         setCallerDisplayName(
    675                 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
    676         setCallCapabilities(connection.getCapabilities());
    677         setVideoProvider(connection.getVideoProvider());
    678         setVideoState(connection.getVideoState());
    679         setRingbackRequested(connection.isRingbackRequested());
    680         setIsVoipAudioMode(connection.getIsVoipAudioMode());
    681         setStatusHints(connection.getStatusHints());
    682 
    683         mConferenceableCalls.clear();
    684         for (String id : connection.getConferenceableConnectionIds()) {
    685             mConferenceableCalls.add(idMapper.getCall(id));
    686         }
    687 
    688         if (mIsUnknown) {
    689             for (Listener l : mListeners) {
    690                 l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection.getState()));
    691             }
    692         } else if (mIsIncoming) {
    693             // We do not handle incoming calls immediately when they are verified by the connection
    694             // service. We allow the caller-info-query code to execute first so that we can read the
    695             // direct-to-voicemail property before deciding if we want to show the incoming call to
    696             // the user or if we want to reject the call.
    697             mDirectToVoicemailQueryPending = true;
    698 
    699             // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
    700             // showing the user the incoming call screen.
    701             mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis(
    702                     mContext.getContentResolver()));
    703         } else {
    704             for (Listener l : mListeners) {
    705                 l.onSuccessfulOutgoingCall(this,
    706                         getStateFromConnectionState(connection.getState()));
    707             }
    708         }
    709     }
    710 
    711     @Override
    712     public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
    713         mCreateConnectionProcessor = null;
    714         clearConnectionService();
    715         setDisconnectCause(disconnectCause);
    716         CallsManager.getInstance().markCallAsDisconnected(this, disconnectCause);
    717 
    718         if (mIsUnknown) {
    719             for (Listener listener : mListeners) {
    720                 listener.onFailedUnknownCall(this);
    721             }
    722         } else if (mIsIncoming) {
    723             for (Listener listener : mListeners) {
    724                 listener.onFailedIncomingCall(this);
    725             }
    726         } else {
    727             for (Listener listener : mListeners) {
    728                 listener.onFailedOutgoingCall(this, disconnectCause);
    729             }
    730         }
    731     }
    732 
    733     /**
    734      * Plays the specified DTMF tone.
    735      */
    736     void playDtmfTone(char digit) {
    737         if (mConnectionService == null) {
    738             Log.w(this, "playDtmfTone() request on a call without a connection service.");
    739         } else {
    740             Log.i(this, "Send playDtmfTone to connection service for call %s", this);
    741             mConnectionService.playDtmfTone(this, digit);
    742         }
    743     }
    744 
    745     /**
    746      * Stops playing any currently playing DTMF tone.
    747      */
    748     void stopDtmfTone() {
    749         if (mConnectionService == null) {
    750             Log.w(this, "stopDtmfTone() request on a call without a connection service.");
    751         } else {
    752             Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
    753             mConnectionService.stopDtmfTone(this);
    754         }
    755     }
    756 
    757     /**
    758      * Attempts to disconnect the call through the connection service.
    759      */
    760     void disconnect() {
    761         // Track that the call is now locally disconnecting.
    762         setLocallyDisconnecting(true);
    763 
    764         if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT ||
    765                 mState == CallState.CONNECTING) {
    766             Log.v(this, "Aborting call %s", this);
    767             abort();
    768         } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
    769             if (mConnectionService == null) {
    770                 Log.e(this, new Exception(), "disconnect() request on a call without a"
    771                         + " connection service.");
    772             } else {
    773                 Log.i(this, "Send disconnect to connection service for call: %s", this);
    774                 // The call isn't officially disconnected until the connection service
    775                 // confirms that the call was actually disconnected. Only then is the
    776                 // association between call and connection service severed, see
    777                 // {@link CallsManager#markCallAsDisconnected}.
    778                 mConnectionService.disconnect(this);
    779             }
    780         }
    781     }
    782 
    783     void abort() {
    784         if (mCreateConnectionProcessor != null) {
    785             mCreateConnectionProcessor.abort();
    786         } else if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT
    787                 || mState == CallState.CONNECTING) {
    788             handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
    789         } else {
    790             Log.v(this, "Cannot abort a call which isn't either PRE_DIAL_WAIT or CONNECTING");
    791         }
    792     }
    793 
    794     /**
    795      * Answers the call if it is ringing.
    796      *
    797      * @param videoState The video state in which to answer the call.
    798      */
    799     void answer(int videoState) {
    800         Preconditions.checkNotNull(mConnectionService);
    801 
    802         // Check to verify that the call is still in the ringing state. A call can change states
    803         // between the time the user hits 'answer' and Telecom receives the command.
    804         if (isRinging("answer")) {
    805             // At this point, we are asking the connection service to answer but we don't assume
    806             // that it will work. Instead, we wait until confirmation from the connectino service
    807             // that the call is in a non-STATE_RINGING state before changing the UI. See
    808             // {@link ConnectionServiceAdapter#setActive} and other set* methods.
    809             mConnectionService.answer(this, videoState);
    810         }
    811     }
    812 
    813     /**
    814      * Rejects the call if it is ringing.
    815      *
    816      * @param rejectWithMessage Whether to send a text message as part of the call rejection.
    817      * @param textMessage An optional text message to send as part of the rejection.
    818      */
    819     void reject(boolean rejectWithMessage, String textMessage) {
    820         Preconditions.checkNotNull(mConnectionService);
    821 
    822         // Check to verify that the call is still in the ringing state. A call can change states
    823         // between the time the user hits 'reject' and Telecomm receives the command.
    824         if (isRinging("reject")) {
    825             mConnectionService.reject(this);
    826         }
    827     }
    828 
    829     /**
    830      * Puts the call on hold if it is currently active.
    831      */
    832     void hold() {
    833         Preconditions.checkNotNull(mConnectionService);
    834 
    835         if (mState == CallState.ACTIVE) {
    836             mConnectionService.hold(this);
    837         }
    838     }
    839 
    840     /**
    841      * Releases the call from hold if it is currently active.
    842      */
    843     void unhold() {
    844         Preconditions.checkNotNull(mConnectionService);
    845 
    846         if (mState == CallState.ON_HOLD) {
    847             mConnectionService.unhold(this);
    848         }
    849     }
    850 
    851     /** Checks if this is a live call or not. */
    852     boolean isAlive() {
    853         switch (mState) {
    854             case CallState.NEW:
    855             case CallState.RINGING:
    856             case CallState.DISCONNECTED:
    857             case CallState.ABORTED:
    858                 return false;
    859             default:
    860                 return true;
    861         }
    862     }
    863 
    864     boolean isActive() {
    865         return mState == CallState.ACTIVE;
    866     }
    867 
    868     Bundle getExtras() {
    869         return mExtras;
    870     }
    871 
    872     void setExtras(Bundle extras) {
    873         mExtras = extras;
    874     }
    875 
    876     /**
    877      * @return the uri of the contact associated with this call.
    878      */
    879     Uri getContactUri() {
    880         if (mCallerInfo == null || !mCallerInfo.contactExists) {
    881             return getHandle();
    882         }
    883         return Contacts.getLookupUri(mCallerInfo.contactIdOrZero, mCallerInfo.lookupKey);
    884     }
    885 
    886     Uri getRingtone() {
    887         return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
    888     }
    889 
    890     void onPostDialWait(String remaining) {
    891         for (Listener l : mListeners) {
    892             l.onPostDialWait(this, remaining);
    893         }
    894     }
    895 
    896     void postDialContinue(boolean proceed) {
    897         mConnectionService.onPostDialContinue(this, proceed);
    898     }
    899 
    900     void conferenceWith(Call otherCall) {
    901         if (mConnectionService == null) {
    902             Log.w(this, "conference requested on a call without a connection service.");
    903         } else {
    904             mConnectionService.conference(this, otherCall);
    905         }
    906     }
    907 
    908     void splitFromConference() {
    909         if (mConnectionService == null) {
    910             Log.w(this, "splitting from conference call without a connection service");
    911         } else {
    912             mConnectionService.splitFromConference(this);
    913         }
    914     }
    915 
    916     void mergeConference() {
    917         if (mConnectionService == null) {
    918             Log.w(this, "merging conference calls without a connection service.");
    919         } else if (can(PhoneCapabilities.MERGE_CONFERENCE)) {
    920             mConnectionService.mergeConference(this);
    921             mWasConferencePreviouslyMerged = true;
    922         }
    923     }
    924 
    925     void swapConference() {
    926         if (mConnectionService == null) {
    927             Log.w(this, "swapping conference calls without a connection service.");
    928         } else if (can(PhoneCapabilities.SWAP_CONFERENCE)) {
    929             mConnectionService.swapConference(this);
    930             switch (mChildCalls.size()) {
    931                 case 1:
    932                     mConferenceLevelActiveCall = mChildCalls.get(0);
    933                     break;
    934                 case 2:
    935                     // swap
    936                     mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ?
    937                             mChildCalls.get(1) : mChildCalls.get(0);
    938                     break;
    939                 default:
    940                     // For anything else 0, or 3+, set it to null since it is impossible to tell.
    941                     mConferenceLevelActiveCall = null;
    942                     break;
    943             }
    944         }
    945     }
    946 
    947     void setParentCall(Call parentCall) {
    948         if (parentCall == this) {
    949             Log.e(this, new Exception(), "setting the parent to self");
    950             return;
    951         }
    952         if (parentCall == mParentCall) {
    953             // nothing to do
    954             return;
    955         }
    956         Preconditions.checkState(parentCall == null || mParentCall == null);
    957 
    958         Call oldParent = mParentCall;
    959         if (mParentCall != null) {
    960             mParentCall.removeChildCall(this);
    961         }
    962         mParentCall = parentCall;
    963         if (mParentCall != null) {
    964             mParentCall.addChildCall(this);
    965         }
    966 
    967         for (Listener l : mListeners) {
    968             l.onParentChanged(this);
    969         }
    970     }
    971 
    972     void setConferenceableCalls(List<Call> conferenceableCalls) {
    973         mConferenceableCalls.clear();
    974         mConferenceableCalls.addAll(conferenceableCalls);
    975 
    976         for (Listener l : mListeners) {
    977             l.onConferenceableCallsChanged(this);
    978         }
    979     }
    980 
    981     List<Call> getConferenceableCalls() {
    982         return mConferenceableCalls;
    983     }
    984 
    985     boolean can(int capability) {
    986         return (mCallCapabilities & capability) == capability;
    987     }
    988 
    989     private void addChildCall(Call call) {
    990         if (!mChildCalls.contains(call)) {
    991             // Set the pseudo-active call to the latest child added to the conference.
    992             // See definition of mConferenceLevelActiveCall for more detail.
    993             mConferenceLevelActiveCall = call;
    994             mChildCalls.add(call);
    995 
    996             for (Listener l : mListeners) {
    997                 l.onChildrenChanged(this);
    998             }
    999         }
   1000     }
   1001 
   1002     private void removeChildCall(Call call) {
   1003         if (mChildCalls.remove(call)) {
   1004             for (Listener l : mListeners) {
   1005                 l.onChildrenChanged(this);
   1006             }
   1007         }
   1008     }
   1009 
   1010     /**
   1011      * Return whether the user can respond to this {@code Call} via an SMS message.
   1012      *
   1013      * @return true if the "Respond via SMS" feature should be enabled
   1014      * for this incoming call.
   1015      *
   1016      * The general rule is that we *do* allow "Respond via SMS" except for
   1017      * the few (relatively rare) cases where we know for sure it won't
   1018      * work, namely:
   1019      *   - a bogus or blank incoming number
   1020      *   - a call from a SIP address
   1021      *   - a "call presentation" that doesn't allow the number to be revealed
   1022      *
   1023      * In all other cases, we allow the user to respond via SMS.
   1024      *
   1025      * Note that this behavior isn't perfect; for example we have no way
   1026      * to detect whether the incoming call is from a landline (with most
   1027      * networks at least), so we still enable this feature even though
   1028      * SMSes to that number will silently fail.
   1029      */
   1030     boolean isRespondViaSmsCapable() {
   1031         if (mState != CallState.RINGING) {
   1032             return false;
   1033         }
   1034 
   1035         if (getHandle() == null) {
   1036             // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
   1037             // other words, the user should not be able to see the incoming phone number.
   1038             return false;
   1039         }
   1040 
   1041         if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
   1042             // The incoming number is actually a URI (i.e. a SIP address),
   1043             // not a regular PSTN phone number, and we can't send SMSes to
   1044             // SIP addresses.
   1045             // (TODO: That might still be possible eventually, though. Is
   1046             // there some SIP-specific equivalent to sending a text message?)
   1047             return false;
   1048         }
   1049 
   1050         // Is there a valid SMS application on the phone?
   1051         if (SmsApplication.getDefaultRespondViaMessageApplication(mContext,
   1052                 true /*updateIfNeeded*/) == null) {
   1053             return false;
   1054         }
   1055 
   1056         // TODO: with some carriers (in certain countries) you *can* actually
   1057         // tell whether a given number is a mobile phone or not. So in that
   1058         // case we could potentially return false here if the incoming call is
   1059         // from a land line.
   1060 
   1061         // If none of the above special cases apply, it's OK to enable the
   1062         // "Respond via SMS" feature.
   1063         return true;
   1064     }
   1065 
   1066     List<String> getCannedSmsResponses() {
   1067         return mCannedSmsResponses;
   1068     }
   1069 
   1070     /**
   1071      * We need to make sure that before we move a call to the disconnected state, it no
   1072      * longer has any parent/child relationships.  We want to do this to ensure that the InCall
   1073      * Service always has the right data in the right order.  We also want to do it in telecom so
   1074      * that the insurance policy lives in the framework side of things.
   1075      */
   1076     private void fixParentAfterDisconnect() {
   1077         setParentCall(null);
   1078     }
   1079 
   1080     /**
   1081      * @return True if the call is ringing, else logs the action name.
   1082      */
   1083     private boolean isRinging(String actionName) {
   1084         if (mState == CallState.RINGING) {
   1085             return true;
   1086         }
   1087 
   1088         Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
   1089         return false;
   1090     }
   1091 
   1092     @SuppressWarnings("rawtypes")
   1093     private void decrementAssociatedCallCount(ServiceBinder binder) {
   1094         if (binder != null) {
   1095             binder.decrementAssociatedCallCount();
   1096         }
   1097     }
   1098 
   1099     /**
   1100      * Looks up contact information based on the current handle.
   1101      */
   1102     private void startCallerInfoLookup() {
   1103         String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
   1104 
   1105         mQueryToken++;  // Updated so that previous queries can no longer set the information.
   1106         mCallerInfo = null;
   1107         if (!TextUtils.isEmpty(number)) {
   1108             Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
   1109             CallerInfoAsyncQuery.startQuery(
   1110                     mQueryToken,
   1111                     mContext,
   1112                     number,
   1113                     sCallerInfoQueryListener,
   1114                     this);
   1115         }
   1116     }
   1117 
   1118     /**
   1119      * Saves the specified caller info if the specified token matches that of the last query
   1120      * that was made.
   1121      *
   1122      * @param callerInfo The new caller information to set.
   1123      * @param token The token used with this query.
   1124      */
   1125     private void setCallerInfo(CallerInfo callerInfo, int token) {
   1126         Preconditions.checkNotNull(callerInfo);
   1127 
   1128         if (mQueryToken == token) {
   1129             mCallerInfo = callerInfo;
   1130             Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
   1131 
   1132             if (mCallerInfo.contactDisplayPhotoUri != null) {
   1133                 Log.d(this, "Searching person uri %s for call %s",
   1134                         mCallerInfo.contactDisplayPhotoUri, this);
   1135                 ContactsAsyncHelper.startObtainPhotoAsync(
   1136                         token,
   1137                         mContext,
   1138                         mCallerInfo.contactDisplayPhotoUri,
   1139                         sPhotoLoadListener,
   1140                         this);
   1141                 // Do not call onCallerInfoChanged yet in this case.  We call it in setPhoto().
   1142             } else {
   1143                 for (Listener l : mListeners) {
   1144                     l.onCallerInfoChanged(this);
   1145                 }
   1146             }
   1147 
   1148             processDirectToVoicemail();
   1149         }
   1150     }
   1151 
   1152     CallerInfo getCallerInfo() {
   1153         return mCallerInfo;
   1154     }
   1155 
   1156     /**
   1157      * Saves the specified photo information if the specified token matches that of the last query.
   1158      *
   1159      * @param photo The photo as a drawable.
   1160      * @param photoIcon The photo as a small icon.
   1161      * @param token The token used with this query.
   1162      */
   1163     private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
   1164         if (mQueryToken == token) {
   1165             mCallerInfo.cachedPhoto = photo;
   1166             mCallerInfo.cachedPhotoIcon = photoIcon;
   1167 
   1168             for (Listener l : mListeners) {
   1169                 l.onCallerInfoChanged(this);
   1170             }
   1171         }
   1172     }
   1173 
   1174     private void maybeLoadCannedSmsResponses() {
   1175         if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
   1176             Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
   1177             mCannedSmsResponsesLoadingStarted = true;
   1178             RespondViaSmsManager.getInstance().loadCannedTextMessages(
   1179                     new Response<Void, List<String>>() {
   1180                         @Override
   1181                         public void onResult(Void request, List<String>... result) {
   1182                             if (result.length > 0) {
   1183                                 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
   1184                                 mCannedSmsResponses = result[0];
   1185                                 for (Listener l : mListeners) {
   1186                                     l.onCannedSmsResponsesLoaded(Call.this);
   1187                                 }
   1188                             }
   1189                         }
   1190 
   1191                         @Override
   1192                         public void onError(Void request, int code, String msg) {
   1193                             Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
   1194                                     msg);
   1195                         }
   1196                     },
   1197                     mContext
   1198             );
   1199         } else {
   1200             Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
   1201         }
   1202     }
   1203 
   1204     /**
   1205      * Sets speakerphone option on when call begins.
   1206      */
   1207     public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
   1208         mSpeakerphoneOn = startWithSpeakerphone;
   1209     }
   1210 
   1211     /**
   1212      * Returns speakerphone option.
   1213      *
   1214      * @return Whether or not speakerphone should be set automatically when call begins.
   1215      */
   1216     public boolean getStartWithSpeakerphoneOn() {
   1217         return mSpeakerphoneOn;
   1218     }
   1219 
   1220     /**
   1221      * Sets a video call provider for the call.
   1222      */
   1223     public void setVideoProvider(IVideoProvider videoProvider) {
   1224         mVideoProvider = videoProvider;
   1225         for (Listener l : mListeners) {
   1226             l.onVideoCallProviderChanged(Call.this);
   1227         }
   1228     }
   1229 
   1230     /**
   1231      * @return Return the {@link Connection.VideoProvider} binder.
   1232      */
   1233     public IVideoProvider getVideoProvider() {
   1234         return mVideoProvider;
   1235     }
   1236 
   1237     /**
   1238      * The current video state for the call.
   1239      * Valid values: see {@link VideoProfile.VideoState}.
   1240      */
   1241     public int getVideoState() {
   1242         return mVideoState;
   1243     }
   1244 
   1245     /**
   1246      * Returns the video states which were applicable over the duration of a call.
   1247      * See {@link VideoProfile} for a list of valid video states.
   1248      *
   1249      * @return The video states applicable over the duration of the call.
   1250      */
   1251     public int getVideoStateHistory() {
   1252         return mVideoStateHistory;
   1253     }
   1254 
   1255     /**
   1256      * Determines the current video state for the call.
   1257      * For an outgoing call determines the desired video state for the call.
   1258      * Valid values: see {@link VideoProfile.VideoState}
   1259      *
   1260      * @param videoState The video state for the call.
   1261      */
   1262     public void setVideoState(int videoState) {
   1263         // Track which video states were applicable over the duration of the call.
   1264         mVideoStateHistory = mVideoStateHistory | videoState;
   1265 
   1266         mVideoState = videoState;
   1267         for (Listener l : mListeners) {
   1268             l.onVideoStateChanged(this);
   1269         }
   1270     }
   1271 
   1272     public boolean getIsVoipAudioMode() {
   1273         return mIsVoipAudioMode;
   1274     }
   1275 
   1276     public void setIsVoipAudioMode(boolean audioModeIsVoip) {
   1277         mIsVoipAudioMode = audioModeIsVoip;
   1278         for (Listener l : mListeners) {
   1279             l.onIsVoipAudioModeChanged(this);
   1280         }
   1281     }
   1282 
   1283     public StatusHints getStatusHints() {
   1284         return mStatusHints;
   1285     }
   1286 
   1287     public void setStatusHints(StatusHints statusHints) {
   1288         mStatusHints = statusHints;
   1289         for (Listener l : mListeners) {
   1290             l.onStatusHintsChanged(this);
   1291         }
   1292     }
   1293 
   1294     public boolean isUnknown() {
   1295         return mIsUnknown;
   1296     }
   1297 
   1298     public void setIsUnknown(boolean isUnknown) {
   1299         mIsUnknown = isUnknown;
   1300     }
   1301 
   1302     /**
   1303      * Determines if this call is in a disconnecting state.
   1304      *
   1305      * @return {@code true} if this call is locally disconnecting.
   1306      */
   1307     public boolean isLocallyDisconnecting() {
   1308         return mIsLocallyDisconnecting;
   1309     }
   1310 
   1311     /**
   1312      * Sets whether this call is in a disconnecting state.
   1313      *
   1314      * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting.
   1315      */
   1316     private void setLocallyDisconnecting(boolean isLocallyDisconnecting) {
   1317         mIsLocallyDisconnecting = isLocallyDisconnecting;
   1318     }
   1319 
   1320     static int getStateFromConnectionState(int state) {
   1321         switch (state) {
   1322             case Connection.STATE_INITIALIZING:
   1323                 return CallState.CONNECTING;
   1324             case Connection.STATE_ACTIVE:
   1325                 return CallState.ACTIVE;
   1326             case Connection.STATE_DIALING:
   1327                 return CallState.DIALING;
   1328             case Connection.STATE_DISCONNECTED:
   1329                 return CallState.DISCONNECTED;
   1330             case Connection.STATE_HOLDING:
   1331                 return CallState.ON_HOLD;
   1332             case Connection.STATE_NEW:
   1333                 return CallState.NEW;
   1334             case Connection.STATE_RINGING:
   1335                 return CallState.RINGING;
   1336         }
   1337         return CallState.DISCONNECTED;
   1338     }
   1339 }
   1340