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