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