Call.java revision 33e453811b7dc0af47301d1aa22384de3d464da4
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
17package com.android.server.telecom;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.drawable.Drawable;
22import android.net.Uri;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.RemoteException;
27import android.os.Trace;
28import android.provider.ContactsContract.Contacts;
29import android.telecom.DisconnectCause;
30import android.telecom.Connection;
31import android.telecom.GatewayInfo;
32import android.telecom.ParcelableConnection;
33import android.telecom.PhoneAccount;
34import android.telecom.PhoneAccountHandle;
35import android.telecom.Response;
36import android.telecom.StatusHints;
37import android.telecom.TelecomManager;
38import android.telecom.VideoProfile;
39import android.telephony.PhoneNumberUtils;
40import android.text.TextUtils;
41import android.os.UserHandle;
42
43import com.android.internal.annotations.VisibleForTesting;
44import com.android.internal.telecom.IVideoProvider;
45import com.android.internal.telephony.CallerInfo;
46import com.android.internal.telephony.SmsApplication;
47import com.android.internal.util.Preconditions;
48
49import java.lang.String;
50import java.util.ArrayList;
51import java.util.Collections;
52import java.util.LinkedList;
53import java.util.List;
54import java.util.Locale;
55import java.util.Objects;
56import java.util.Set;
57import 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
65public 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        Log.event(this, Log.Events.CONNECTION_EVENT, event);
2117        if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) {
2118            mIsRemotelyHeld = true;
2119            Log.event(this, Log.Events.REMOTELY_HELD);
2120            // Inform listeners of the fact that a call hold tone was received.  This will trigger
2121            // the CallAudioManager to play a tone via the InCallTonePlayer.
2122            for (Listener l : mListeners) {
2123                l.onHoldToneRequested(this);
2124            }
2125        } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) {
2126            mIsRemotelyHeld = false;
2127            Log.event(this, Log.Events.REMOTELY_UNHELD);
2128            for (Listener l : mListeners) {
2129                l.onHoldToneRequested(this);
2130            }
2131        } else {
2132            for (Listener l : mListeners) {
2133                l.onConnectionEvent(this, event, extras);
2134            }
2135        }
2136    }
2137
2138    /**
2139     * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either
2140     * remotely or locally.
2141     *
2142     * @param capabilities The {@link Connection} capabilities for the call.
2143     * @return {@code true} if video is supported, {@code false} otherwise.
2144     */
2145    private boolean doesCallSupportVideo(int capabilities) {
2146        return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 ||
2147                (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0;
2148    }
2149
2150    /**
2151     * Remove any video capabilities set on a {@link Connection} capabilities bitmask.
2152     *
2153     * @param capabilities The capabilities.
2154     * @return The bitmask with video capabilities removed.
2155     */
2156    private int removeVideoCapabilities(int capabilities) {
2157        return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL |
2158                Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
2159    }
2160}
2161