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