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