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