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