Call.java revision 36c62f3e3cc155e950de8b1a1f1109d38fb32d61
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 android.telecom;
18
19import android.annotation.SystemApi;
20import android.net.Uri;
21import android.os.Bundle;
22
23import java.lang.String;
24import java.util.ArrayList;
25import java.util.Collections;
26import java.util.List;
27import java.util.Map;
28import java.util.Objects;
29import java.util.concurrent.CopyOnWriteArrayList;
30
31/**
32 * Represents an ongoing phone call that the in-call app should present to the user.
33 *
34 * {@hide}
35 */
36@SystemApi
37public final class Call {
38    /**
39     * The state of a {@code Call} when newly created.
40     */
41    public static final int STATE_NEW = 0;
42
43    /**
44     * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected.
45     */
46    public static final int STATE_DIALING = 1;
47
48    /**
49     * The state of an incoming {@code Call} when ringing locally, but not yet connected.
50     */
51    public static final int STATE_RINGING = 2;
52
53    /**
54     * The state of a {@code Call} when in a holding state.
55     */
56    public static final int STATE_HOLDING = 3;
57
58    /**
59     * The state of a {@code Call} when actively supporting conversation.
60     */
61    public static final int STATE_ACTIVE = 4;
62
63    /**
64     * The state of a {@code Call} when no further voice or other communication is being
65     * transmitted, the remote side has been or will inevitably be informed that the {@code Call}
66     * is no longer active, and the local data transport has or inevitably will release resources
67     * associated with this {@code Call}.
68     */
69    public static final int STATE_DISCONNECTED = 7;
70
71    /**
72     * The state of an outgoing {@code Call}, but waiting for user input before proceeding.
73     */
74    public static final int STATE_PRE_DIAL_WAIT = 8;
75
76    /**
77     * The initial state of an outgoing {@code Call}.
78     * Common transitions are to {@link #STATE_DIALING} state for a successful call or
79     * {@link #STATE_DISCONNECTED} if it failed.
80     */
81    public static final int STATE_CONNECTING = 9;
82
83    /**
84     * The state of a {@code Call} when the user has initiated a disconnection of the call, but the
85     * call has not yet been disconnected by the underlying {@code ConnectionService}.  The next
86     * state of the call is (potentially) {@link #STATE_DISCONNECTED}.
87     */
88    public static final int STATE_DISCONNECTING = 10;
89
90    /**
91     * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
92     * extras. Used to pass the phone accounts to display on the front end to the user in order to
93     * select phone accounts to (for example) place a call.
94     *
95     * @hide
96     */
97    public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
98
99    public static class Details {
100        private final Uri mHandle;
101        private final int mHandlePresentation;
102        private final String mCallerDisplayName;
103        private final int mCallerDisplayNamePresentation;
104        private final PhoneAccountHandle mAccountHandle;
105        private final int mCallCapabilities;
106        private final int mCallProperties;
107        private final DisconnectCause mDisconnectCause;
108        private final long mConnectTimeMillis;
109        private final GatewayInfo mGatewayInfo;
110        private final int mVideoState;
111        private final StatusHints mStatusHints;
112        private final Bundle mExtras;
113
114        /**
115         * @return The handle (e.g., phone number) to which the {@code Call} is currently
116         * connected.
117         */
118        public Uri getHandle() {
119            return mHandle;
120        }
121
122        /**
123         * @return The presentation requirements for the handle. See
124         * {@link TelecomManager} for valid values.
125         */
126        public int getHandlePresentation() {
127            return mHandlePresentation;
128        }
129
130        /**
131         * @return The display name for the caller.
132         */
133        public String getCallerDisplayName() {
134            return mCallerDisplayName;
135        }
136
137        /**
138         * @return The presentation requirements for the caller display name. See
139         * {@link TelecomManager} for valid values.
140         */
141        public int getCallerDisplayNamePresentation() {
142            return mCallerDisplayNamePresentation;
143        }
144
145        /**
146         * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being
147         * routed.
148         */
149        public PhoneAccountHandle getAccountHandle() {
150            return mAccountHandle;
151        }
152
153        /**
154         * @return A bitmask of the capabilities of the {@code Call}, as defined in
155         *         {@link PhoneCapabilities}.
156         */
157        public int getCallCapabilities() {
158            return mCallCapabilities;
159        }
160
161        /**
162         * @return A bitmask of the properties of the {@code Call}, as defined in
163         *         {@link CallProperties}.
164         */
165        public int getCallProperties() {
166            return mCallProperties;
167        }
168
169        /**
170         * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
171         * by {@link android.telecom.DisconnectCause}.
172         */
173        public DisconnectCause getDisconnectCause() {
174            return mDisconnectCause;
175        }
176
177        /**
178         * @return The time the {@code Call} has been connected. This information is updated
179         * periodically, but user interfaces should not rely on this to display any "call time
180         * clock".
181         */
182        public long getConnectTimeMillis() {
183            return mConnectTimeMillis;
184        }
185
186        /**
187         * @return Information about any calling gateway the {@code Call} may be using.
188         */
189        public GatewayInfo getGatewayInfo() {
190            return mGatewayInfo;
191        }
192
193        /**
194         * @return The video state of the {@code Call}.
195         */
196        public int getVideoState() {
197            return mVideoState;
198        }
199
200        /**
201         * @return The current {@link android.telecom.StatusHints}, or {@code null} if none
202         * have been set.
203         */
204        public StatusHints getStatusHints() {
205            return mStatusHints;
206        }
207
208        /**
209         * @return A bundle extras to pass with the call
210         */
211        public Bundle getExtras() {
212            return mExtras;
213        }
214
215        @Override
216        public boolean equals(Object o) {
217            if (o instanceof Details) {
218                Details d = (Details) o;
219                return
220                        Objects.equals(mHandle, d.mHandle) &&
221                        Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
222                        Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
223                        Objects.equals(mCallerDisplayNamePresentation,
224                                d.mCallerDisplayNamePresentation) &&
225                        Objects.equals(mAccountHandle, d.mAccountHandle) &&
226                        Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
227                        Objects.equals(mCallProperties, d.mCallProperties) &&
228                        Objects.equals(mDisconnectCause, d.mDisconnectCause) &&
229                        Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
230                        Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
231                        Objects.equals(mVideoState, d.mVideoState) &&
232                        Objects.equals(mStatusHints, d.mStatusHints) &&
233                        Objects.equals(mExtras, d.mExtras);
234            }
235            return false;
236        }
237
238        @Override
239        public int hashCode() {
240            return
241                    Objects.hashCode(mHandle) +
242                    Objects.hashCode(mHandlePresentation) +
243                    Objects.hashCode(mCallerDisplayName) +
244                    Objects.hashCode(mCallerDisplayNamePresentation) +
245                    Objects.hashCode(mAccountHandle) +
246                    Objects.hashCode(mCallCapabilities) +
247                    Objects.hashCode(mCallProperties) +
248                    Objects.hashCode(mDisconnectCause) +
249                    Objects.hashCode(mConnectTimeMillis) +
250                    Objects.hashCode(mGatewayInfo) +
251                    Objects.hashCode(mVideoState) +
252                    Objects.hashCode(mStatusHints) +
253                    Objects.hashCode(mExtras);
254        }
255
256        /** {@hide} */
257        public Details(
258                Uri handle,
259                int handlePresentation,
260                String callerDisplayName,
261                int callerDisplayNamePresentation,
262                PhoneAccountHandle accountHandle,
263                int capabilities,
264                int properties,
265                DisconnectCause disconnectCause,
266                long connectTimeMillis,
267                GatewayInfo gatewayInfo,
268                int videoState,
269                StatusHints statusHints,
270                Bundle extras) {
271            mHandle = handle;
272            mHandlePresentation = handlePresentation;
273            mCallerDisplayName = callerDisplayName;
274            mCallerDisplayNamePresentation = callerDisplayNamePresentation;
275            mAccountHandle = accountHandle;
276            mCallCapabilities = capabilities;
277            mCallProperties = properties;
278            mDisconnectCause = disconnectCause;
279            mConnectTimeMillis = connectTimeMillis;
280            mGatewayInfo = gatewayInfo;
281            mVideoState = videoState;
282            mStatusHints = statusHints;
283            mExtras = extras;
284        }
285    }
286
287    public static abstract class Listener {
288        /**
289         * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
290         *
291         * @param call The {@code Call} invoking this method.
292         * @param state The new state of the {@code Call}.
293         */
294        public void onStateChanged(Call call, int state) {}
295
296        /**
297         * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
298         *
299         * @param call The {@code Call} invoking this method.
300         * @param parent The new parent of the {@code Call}.
301         */
302        public void onParentChanged(Call call, Call parent) {}
303
304        /**
305         * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
306         *
307         * @param call The {@code Call} invoking this method.
308         * @param children The new children of the {@code Call}.
309         */
310        public void onChildrenChanged(Call call, List<Call> children) {}
311
312        /**
313         * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
314         *
315         * @param call The {@code Call} invoking this method.
316         * @param details A {@code Details} object describing the {@code Call}.
317         */
318        public void onDetailsChanged(Call call, Details details) {}
319
320        /**
321         * Invoked when the text messages that can be used as responses to the incoming
322         * {@code Call} are loaded from the relevant database.
323         * See {@link #getCannedTextResponses()}.
324         *
325         * @param call The {@code Call} invoking this method.
326         * @param cannedTextResponses The text messages useable as responses.
327         */
328        public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
329
330        /**
331         * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
332         * character. This causes the post-dial signals to stop pending user confirmation. An
333         * implementation should present this choice to the user and invoke
334         * {@link #postDialContinue(boolean)} when the user makes the choice.
335         *
336         * @param call The {@code Call} invoking this method.
337         * @param remainingPostDialSequence The post-dial characters that remain to be sent.
338         */
339        public void onPostDialWait(Call call, String remainingPostDialSequence) {}
340
341        /**
342         * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed.
343         *
344         * @param call The {@code Call} invoking this method.
345         * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
346         * @hide
347         */
348        public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
349
350        /**
351         * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
352         * up their UI for the {@code Call} in response to state transitions. Specifically,
353         * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
354         * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
355         * clients should wait for this method to be invoked.
356         *
357         * @param call The {@code Call} being destroyed.
358         */
359        public void onCallDestroyed(Call call) {}
360
361        /**
362         * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be
363         * conferenced.
364         *
365         * @param call The {@code Call} being updated.
366         * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be
367         *          conferenced.
368         */
369        public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
370    }
371
372    private final Phone mPhone;
373    private final String mTelecomCallId;
374    private final InCallAdapter mInCallAdapter;
375    private final List<String> mChildrenIds = new ArrayList<>();
376    private final List<Call> mChildren = new ArrayList<>();
377    private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
378    private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
379    private final List<Call> mConferenceableCalls = new ArrayList<>();
380    private final List<Call> mUnmodifiableConferenceableCalls =
381            Collections.unmodifiableList(mConferenceableCalls);
382
383    private boolean mChildrenCached;
384    private String mParentId = null;
385    private int mState;
386    private List<String> mCannedTextResponses = null;
387    private String mRemainingPostDialSequence;
388    private InCallService.VideoCall mVideoCall;
389    private Details mDetails;
390
391    /**
392     * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
393     *
394     * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
395     * remaining or this {@code Call} is not in a post-dial state.
396     */
397    public String getRemainingPostDialSequence() {
398        return mRemainingPostDialSequence;
399    }
400
401    /**
402     * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
403     * @param videoState The video state in which to answer the call.
404     */
405    public void answer(int videoState) {
406        mInCallAdapter.answerCall(mTelecomCallId, videoState);
407    }
408
409    /**
410     * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
411     *
412     * @param rejectWithMessage Whether to reject with a text message.
413     * @param textMessage An optional text message with which to respond.
414     */
415    public void reject(boolean rejectWithMessage, String textMessage) {
416        mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage);
417    }
418
419    /**
420     * Instructs this {@code Call} to disconnect.
421     */
422    public void disconnect() {
423        mInCallAdapter.disconnectCall(mTelecomCallId);
424    }
425
426    /**
427     * Instructs this {@code Call} to go on hold.
428     */
429    public void hold() {
430        mInCallAdapter.holdCall(mTelecomCallId);
431    }
432
433    /**
434     * Instructs this {@link #STATE_HOLDING} call to release from hold.
435     */
436    public void unhold() {
437        mInCallAdapter.unholdCall(mTelecomCallId);
438    }
439
440    /**
441     * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
442     *
443     * Any other currently playing DTMF tone in the specified call is immediately stopped.
444     *
445     * @param digit A character representing the DTMF digit for which to play the tone. This
446     *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
447     */
448    public void playDtmfTone(char digit) {
449        mInCallAdapter.playDtmfTone(mTelecomCallId, digit);
450    }
451
452    /**
453     * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
454     * currently playing.
455     *
456     * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
457     * currently playing, this method will do nothing.
458     */
459    public void stopDtmfTone() {
460        mInCallAdapter.stopDtmfTone(mTelecomCallId);
461    }
462
463    /**
464     * Instructs this {@code Call} to continue playing a post-dial DTMF string.
465     *
466     * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
467     * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
468     *
469     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
470     * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
471     *
472     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
473     * {@code Call} will pause playing the tones and notify listeners via
474     * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app
475     * should display to the user an indication of this state and an affordance to continue
476     * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
477     * app should invoke the {@link #postDialContinue(boolean)} method.
478     *
479     * @param proceed Whether or not to continue with the post-dial sequence.
480     */
481    public void postDialContinue(boolean proceed) {
482        mInCallAdapter.postDialContinue(mTelecomCallId, proceed);
483    }
484
485    /**
486     * Notifies this {@code Call} that an account has been selected and to proceed with placing
487     * an outgoing call. Optionally sets this account as the default account.
488     */
489    public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
490        mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle, setDefault);
491
492    }
493
494    /**
495     * Instructs this {@code Call} to enter a conference.
496     *
497     * @param callToConferenceWith The other call with which to conference.
498     */
499    public void conference(Call callToConferenceWith) {
500        if (callToConferenceWith != null) {
501            mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId);
502        }
503    }
504
505    /**
506     * Instructs this {@code Call} to split from any conference call with which it may be
507     * connected.
508     */
509    public void splitFromConference() {
510        mInCallAdapter.splitFromConference(mTelecomCallId);
511    }
512
513    /**
514     * Merges the calls within this conference. See {@link PhoneCapabilities#MERGE_CONFERENCE}.
515     */
516    public void mergeConference() {
517        mInCallAdapter.mergeConference(mTelecomCallId);
518    }
519
520    /**
521     * Swaps the calls within this conference. See {@link PhoneCapabilities#SWAP_CONFERENCE}.
522     */
523    public void swapConference() {
524        mInCallAdapter.swapConference(mTelecomCallId);
525    }
526
527    /**
528     * Obtains the parent of this {@code Call} in a conference, if any.
529     *
530     * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
531     * child of any conference {@code Call}s.
532     */
533    public Call getParent() {
534        if (mParentId != null) {
535            return mPhone.internalGetCallByTelecomId(mParentId);
536        }
537        return null;
538    }
539
540    /**
541     * Obtains the children of this conference {@code Call}, if any.
542     *
543     * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
544     * {@code List} otherwise.
545     */
546    public List<Call> getChildren() {
547        if (!mChildrenCached) {
548            mChildrenCached = true;
549            mChildren.clear();
550
551            for(String id : mChildrenIds) {
552                Call call = mPhone.internalGetCallByTelecomId(id);
553                if (call == null) {
554                    // At least one child was still not found, so do not save true for "cached"
555                    mChildrenCached = false;
556                } else {
557                    mChildren.add(call);
558                }
559            }
560        }
561
562        return mUnmodifiableChildren;
563    }
564
565    /**
566     * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference.
567     *
568     * @return The list of conferenceable {@code Call}s.
569     */
570    public List<Call> getConferenceableCalls() {
571        return mUnmodifiableConferenceableCalls;
572    }
573
574    /**
575     * Obtains the state of this {@code Call}.
576     *
577     * @return A state value, chosen from the {@code STATE_*} constants.
578     */
579    public int getState() {
580        return mState;
581    }
582
583    /**
584     * Obtains a list of canned, pre-configured message responses to present to the user as
585     * ways of rejecting this {@code Call} using via a text message.
586     *
587     * @see #reject(boolean, String)
588     *
589     * @return A list of canned text message responses.
590     */
591    public List<String> getCannedTextResponses() {
592        return mCannedTextResponses;
593    }
594
595    /**
596     * Obtains an object that can be used to display video from this {@code Call}.
597     *
598     * @return An {@code Call.VideoCall}.
599     * @hide
600     */
601    public InCallService.VideoCall getVideoCall() {
602        return mVideoCall;
603    }
604
605    /**
606     * Obtains an object containing call details.
607     *
608     * @return A {@link Details} object. Depending on the state of the {@code Call}, the
609     * result may be {@code null}.
610     */
611    public Details getDetails() {
612        return mDetails;
613    }
614
615    /**
616     * Adds a listener to this {@code Call}.
617     *
618     * @param listener A {@code Listener}.
619     */
620    public void addListener(Listener listener) {
621        mListeners.add(listener);
622    }
623
624    /**
625     * Removes a listener from this {@code Call}.
626     *
627     * @param listener A {@code Listener}.
628     */
629    public void removeListener(Listener listener) {
630        if (listener != null) {
631            mListeners.remove(listener);
632        }
633    }
634
635    /** {@hide} */
636    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
637        mPhone = phone;
638        mTelecomCallId = telecomCallId;
639        mInCallAdapter = inCallAdapter;
640        mState = STATE_NEW;
641    }
642
643    /** {@hide} */
644    final String internalGetCallId() {
645        return mTelecomCallId;
646    }
647
648    /** {@hide} */
649    final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
650        // First, we update the internal state as far as possible before firing any updates.
651        Details details = new Details(
652                parcelableCall.getHandle(),
653                parcelableCall.getHandlePresentation(),
654                parcelableCall.getCallerDisplayName(),
655                parcelableCall.getCallerDisplayNamePresentation(),
656                parcelableCall.getAccountHandle(),
657                parcelableCall.getCapabilities(),
658                parcelableCall.getProperties(),
659                parcelableCall.getDisconnectCause(),
660                parcelableCall.getConnectTimeMillis(),
661                parcelableCall.getGatewayInfo(),
662                parcelableCall.getVideoState(),
663                parcelableCall.getStatusHints(),
664                parcelableCall.getExtras());
665        boolean detailsChanged = !Objects.equals(mDetails, details);
666        if (detailsChanged) {
667            mDetails = details;
668        }
669
670        boolean cannedTextResponsesChanged = false;
671        if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
672                && !parcelableCall.getCannedSmsResponses().isEmpty()) {
673            mCannedTextResponses =
674                    Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
675        }
676
677        boolean videoCallChanged = !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
678        if (videoCallChanged) {
679            mVideoCall = parcelableCall.getVideoCall();
680        }
681
682        int state = stateFromParcelableCallState(parcelableCall.getState());
683        boolean stateChanged = mState != state;
684        if (stateChanged) {
685            mState = state;
686        }
687
688        String parentId = parcelableCall.getParentCallId();
689        boolean parentChanged = !Objects.equals(mParentId, parentId);
690        if (parentChanged) {
691            mParentId = parentId;
692        }
693
694        List<String> childCallIds = parcelableCall.getChildCallIds();
695        boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds);
696        if (childrenChanged) {
697            mChildrenIds.clear();
698            mChildrenIds.addAll(parcelableCall.getChildCallIds());
699            mChildrenCached = false;
700        }
701
702        List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds();
703        List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size());
704        for (String otherId : conferenceableCallIds) {
705            if (callIdMap.containsKey(otherId)) {
706                conferenceableCalls.add(callIdMap.get(otherId));
707            }
708        }
709
710        if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) {
711            mConferenceableCalls.clear();
712            mConferenceableCalls.addAll(conferenceableCalls);
713            fireConferenceableCallsChanged();
714        }
715
716        // Now we fire updates, ensuring that any client who listens to any of these notifications
717        // gets the most up-to-date state.
718
719        if (stateChanged) {
720            fireStateChanged(mState);
721        }
722        if (detailsChanged) {
723            fireDetailsChanged(mDetails);
724        }
725        if (cannedTextResponsesChanged) {
726            fireCannedTextResponsesLoaded(mCannedTextResponses);
727        }
728        if (videoCallChanged) {
729            fireVideoCallChanged(mVideoCall);
730        }
731        if (parentChanged) {
732            fireParentChanged(getParent());
733        }
734        if (childrenChanged) {
735            fireChildrenChanged(getChildren());
736        }
737
738        // If we have transitioned to DISCONNECTED, that means we need to notify clients and
739        // remove ourselves from the Phone. Note that we do this after completing all state updates
740        // so a client can cleanly transition all their UI to the state appropriate for a
741        // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
742        if (mState == STATE_DISCONNECTED) {
743            fireCallDestroyed();
744            mPhone.internalRemoveCall(this);
745        }
746    }
747
748    /** {@hide} */
749    final void internalSetPostDialWait(String remaining) {
750        mRemainingPostDialSequence = remaining;
751        firePostDialWait(mRemainingPostDialSequence);
752    }
753
754    /** {@hide} */
755    final void internalSetDisconnected() {
756        if (mState != Call.STATE_DISCONNECTED) {
757            mState = Call.STATE_DISCONNECTED;
758            fireStateChanged(mState);
759            fireCallDestroyed();
760            mPhone.internalRemoveCall(this);
761        }
762    }
763
764    private void fireStateChanged(int newState) {
765        for (Listener listener : mListeners) {
766            listener.onStateChanged(this, newState);
767        }
768    }
769
770    private void fireParentChanged(Call newParent) {
771        for (Listener listener : mListeners) {
772            listener.onParentChanged(this, newParent);
773        }
774    }
775
776    private void fireChildrenChanged(List<Call> children) {
777        for (Listener listener : mListeners) {
778            listener.onChildrenChanged(this, children);
779        }
780    }
781
782    private void fireDetailsChanged(Details details) {
783        for (Listener listener : mListeners) {
784            listener.onDetailsChanged(this, details);
785        }
786    }
787
788    private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
789        for (Listener listener : mListeners) {
790            listener.onCannedTextResponsesLoaded(this, cannedTextResponses);
791        }
792    }
793
794    private void fireVideoCallChanged(InCallService.VideoCall videoCall) {
795        for (Listener listener : mListeners) {
796            listener.onVideoCallChanged(this, videoCall);
797        }
798    }
799
800    private void firePostDialWait(String remainingPostDialSequence) {
801        for (Listener listener : mListeners) {
802            listener.onPostDialWait(this, remainingPostDialSequence);
803        }
804    }
805
806    private void fireCallDestroyed() {
807        for (Listener listener : mListeners) {
808            listener.onCallDestroyed(this);
809        }
810    }
811
812    private void fireConferenceableCallsChanged() {
813        for (Listener listener : mListeners) {
814            listener.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
815        }
816    }
817
818    private int stateFromParcelableCallState(int parcelableCallState) {
819        switch (parcelableCallState) {
820            case CallState.NEW:
821                return STATE_NEW;
822            case CallState.CONNECTING:
823                return STATE_CONNECTING;
824            case CallState.PRE_DIAL_WAIT:
825                return STATE_PRE_DIAL_WAIT;
826            case CallState.DIALING:
827                return STATE_DIALING;
828            case CallState.RINGING:
829                return STATE_RINGING;
830            case CallState.ACTIVE:
831                return STATE_ACTIVE;
832            case CallState.ON_HOLD:
833                return STATE_HOLDING;
834            case CallState.DISCONNECTED:
835                return STATE_DISCONNECTED;
836            case CallState.ABORTED:
837                return STATE_DISCONNECTED;
838            case CallState.DISCONNECTING:
839                return STATE_DISCONNECTING;
840            default:
841                Log.wtf(this, "Unrecognized CallState %s", parcelableCallState);
842                return STATE_NEW;
843        }
844    }
845}
846