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