Call.java revision 876dbfb4767da4a2ba5459d5b78fb6eb55e4516f
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;
22import android.os.Handler;
23
24import java.lang.String;
25import java.util.ArrayList;
26import java.util.Collections;
27import java.util.List;
28import java.util.Map;
29import java.util.Objects;
30import java.util.concurrent.CopyOnWriteArrayList;
31
32/**
33 * Represents an ongoing phone call that the in-call app should present to the user.
34 */
35public final class Call {
36    /**
37     * The state of a {@code Call} when newly created.
38     */
39    public static final int STATE_NEW = 0;
40
41    /**
42     * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected.
43     */
44    public static final int STATE_DIALING = 1;
45
46    /**
47     * The state of an incoming {@code Call} when ringing locally, but not yet connected.
48     */
49    public static final int STATE_RINGING = 2;
50
51    /**
52     * The state of a {@code Call} when in a holding state.
53     */
54    public static final int STATE_HOLDING = 3;
55
56    /**
57     * The state of a {@code Call} when actively supporting conversation.
58     */
59    public static final int STATE_ACTIVE = 4;
60
61    /**
62     * The state of a {@code Call} when no further voice or other communication is being
63     * transmitted, the remote side has been or will inevitably be informed that the {@code Call}
64     * is no longer active, and the local data transport has or inevitably will release resources
65     * associated with this {@code Call}.
66     */
67    public static final int STATE_DISCONNECTED = 7;
68
69    /**
70     * The state of an outgoing {@code Call} when waiting on user to select a
71     * {@link PhoneAccount} through which to place the call.
72     */
73    public static final int STATE_SELECT_PHONE_ACCOUNT = 8;
74
75    /**
76     * @hide
77     * @deprecated use STATE_SELECT_PHONE_ACCOUNT.
78     */
79    @Deprecated
80    @SystemApi
81    public static final int STATE_PRE_DIAL_WAIT = STATE_SELECT_PHONE_ACCOUNT;
82
83    /**
84     * The initial state of an outgoing {@code Call}.
85     * Common transitions are to {@link #STATE_DIALING} state for a successful call or
86     * {@link #STATE_DISCONNECTED} if it failed.
87     */
88    public static final int STATE_CONNECTING = 9;
89
90    /**
91     * The state of a {@code Call} when the user has initiated a disconnection of the call, but the
92     * call has not yet been disconnected by the underlying {@code ConnectionService}.  The next
93     * state of the call is (potentially) {@link #STATE_DISCONNECTED}.
94     */
95    public static final int STATE_DISCONNECTING = 10;
96
97    /**
98     * The state of an external call which is in the process of being pulled from a remote device to
99     * the local device.
100     * <p>
101     * A call can only be in this state if the {@link Details#PROPERTY_IS_EXTERNAL_CALL} property
102     * and {@link Details#CAPABILITY_CAN_PULL_CALL} capability are set on the call.
103     * <p>
104     * An {@link InCallService} will only see this state if it has the
105     * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
106     * manifest.
107     */
108    public static final int STATE_PULLING_CALL = 11;
109
110    /**
111     * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
112     * extras. Used to pass the phone accounts to display on the front end to the user in order to
113     * select phone accounts to (for example) place a call.
114     */
115    public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
116
117    public static class Details {
118
119        /** Call can currently be put on hold or unheld. */
120        public static final int CAPABILITY_HOLD = 0x00000001;
121
122        /** Call supports the hold feature. */
123        public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
124
125        /**
126         * Calls within a conference can be merged. A {@link ConnectionService} has the option to
127         * add a {@link Conference} call before the child {@link Connection}s are merged. This is how
128         * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
129         * capability allows a merge button to be shown while the conference call is in the foreground
130         * of the in-call UI.
131         * <p>
132         * This is only intended for use by a {@link Conference}.
133         */
134        public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
135
136        /**
137         * Calls within a conference can be swapped between foreground and background.
138         * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
139         * <p>
140         * This is only intended for use by a {@link Conference}.
141         */
142        public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
143
144        /**
145         * @hide
146         */
147        public static final int CAPABILITY_UNUSED_1 = 0x00000010;
148
149        /** Call supports responding via text option. */
150        public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
151
152        /** Call can be muted. */
153        public static final int CAPABILITY_MUTE = 0x00000040;
154
155        /**
156         * Call supports conference call management. This capability only applies to {@link Conference}
157         * calls which can have {@link Connection}s as children.
158         */
159        public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
160
161        /**
162         * Local device supports receiving video.
163         */
164        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
165
166        /**
167         * Local device supports transmitting video.
168         */
169        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
170
171        /**
172         * Local device supports bidirectional video calling.
173         */
174        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
175                CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
176
177        /**
178         * Remote device supports receiving video.
179         */
180        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
181
182        /**
183         * Remote device supports transmitting video.
184         */
185        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
186
187        /**
188         * Remote device supports bidirectional video calling.
189         */
190        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
191                CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
192
193        /**
194         * Call is able to be separated from its parent {@code Conference}, if any.
195         */
196        public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
197
198        /**
199         * Call is able to be individually disconnected when in a {@code Conference}.
200         */
201        public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
202
203        /**
204         * Speed up audio setup for MT call.
205         * @hide
206         */
207        public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
208
209        /**
210         * Call can be upgraded to a video call.
211         * @hide
212         */
213        public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
214
215        /**
216         * For video calls, indicates whether the outgoing video for the call can be paused using
217         * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
218         */
219        public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
220
221        /**
222         * Call sends responses through connection.
223         * @hide
224         */
225        public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00200000;
226
227        /**
228         * When set, prevents a video {@code Call} from being downgraded to an audio-only call.
229         * <p>
230         * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
231         * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
232         * downgraded from a video call back to a VideoState of
233         * {@link VideoProfile#STATE_AUDIO_ONLY}.
234         * <p>
235         * Intuitively, a call which can be downgraded to audio should also have local and remote
236         * video
237         * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
238         * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
239         */
240        public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000;
241
242        /**
243         * When set for an external call, indicates that this {@code Call} can be pulled from a
244         * remote device to the current device.
245         * <p>
246         * Should only be set on a {@code Call} where {@link #PROPERTY_IS_EXTERNAL_CALL} is set.
247         * <p>
248         * An {@link InCallService} will only see calls with this capability if it has the
249         * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
250         * in its manifest.
251         * <p>
252         * See {@link Connection#CAPABILITY_CAN_PULL_CALL} and
253         * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
254         */
255        public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
256
257        //******************************************************************************************
258        // Next CAPABILITY value: 0x01000000
259        //******************************************************************************************
260
261        /**
262         * Whether the call is currently a conference.
263         */
264        public static final int PROPERTY_CONFERENCE = 0x00000001;
265
266        /**
267         * Whether the call is a generic conference, where we do not know the precise state of
268         * participants in the conference (eg. on CDMA).
269         */
270        public static final int PROPERTY_GENERIC_CONFERENCE = 0x00000002;
271
272        /**
273         * Whether the call is made while the device is in emergency callback mode.
274         */
275        public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 0x00000004;
276
277        /**
278         * Connection is using WIFI.
279         */
280        public static final int PROPERTY_WIFI = 0x00000008;
281
282        /**
283         * Call is using high definition audio.
284         */
285        public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010;
286
287        /**
288         * Whether the call is associated with the work profile.
289         */
290        public static final int PROPERTY_WORK_CALL = 0x00000020;
291
292        /**
293         * When set, indicates that this {@code Call} does not actually exist locally for the
294         * {@link ConnectionService}.
295         * <p>
296         * Consider, for example, a scenario where a user has two phones with the same phone number.
297         * When a user places a call on one device, the telephony stack can represent that call on
298         * the other device by adding it to the {@link ConnectionService} with the
299         * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL} capability set.
300         * <p>
301         * An {@link InCallService} will only see calls with this property if it has the
302         * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
303         * in its manifest.
304         * <p>
305         * See {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
306         */
307        public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040;
308
309        //******************************************************************************************
310        // Next PROPERTY value: 0x00000100
311        //******************************************************************************************
312
313        private final String mTelecomCallId;
314        private final Uri mHandle;
315        private final int mHandlePresentation;
316        private final String mCallerDisplayName;
317        private final int mCallerDisplayNamePresentation;
318        private final PhoneAccountHandle mAccountHandle;
319        private final int mCallCapabilities;
320        private final int mCallProperties;
321        private final DisconnectCause mDisconnectCause;
322        private final long mConnectTimeMillis;
323        private final GatewayInfo mGatewayInfo;
324        private final int mVideoState;
325        private final StatusHints mStatusHints;
326        private final Bundle mExtras;
327        private final Bundle mIntentExtras;
328
329        /**
330         * Whether the supplied capabilities  supports the specified capability.
331         *
332         * @param capabilities A bit field of capabilities.
333         * @param capability The capability to check capabilities for.
334         * @return Whether the specified capability is supported.
335         */
336        public static boolean can(int capabilities, int capability) {
337            return (capabilities & capability) == capability;
338        }
339
340        /**
341         * Whether the capabilities of this {@code Details} supports the specified capability.
342         *
343         * @param capability The capability to check capabilities for.
344         * @return Whether the specified capability is supported.
345         */
346        public boolean can(int capability) {
347            return can(mCallCapabilities, capability);
348        }
349
350        /**
351         * Render a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
352         *
353         * @param capabilities A capability bit field.
354         * @return A human readable string representation.
355         */
356        public static String capabilitiesToString(int capabilities) {
357            StringBuilder builder = new StringBuilder();
358            builder.append("[Capabilities:");
359            if (can(capabilities, CAPABILITY_HOLD)) {
360                builder.append(" CAPABILITY_HOLD");
361            }
362            if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
363                builder.append(" CAPABILITY_SUPPORT_HOLD");
364            }
365            if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
366                builder.append(" CAPABILITY_MERGE_CONFERENCE");
367            }
368            if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
369                builder.append(" CAPABILITY_SWAP_CONFERENCE");
370            }
371            if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
372                builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
373            }
374            if (can(capabilities, CAPABILITY_MUTE)) {
375                builder.append(" CAPABILITY_MUTE");
376            }
377            if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
378                builder.append(" CAPABILITY_MANAGE_CONFERENCE");
379            }
380            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
381                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
382            }
383            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
384                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
385            }
386            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
387                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
388            }
389            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
390                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
391            }
392            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
393                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
394            }
395            if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
396                builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
397            }
398            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
399                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
400            }
401            if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
402                builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
403            }
404            if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
405                builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
406            }
407            if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
408                builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
409            }
410            if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
411                builder.append(" CAPABILITY_CAN_PULL_CALL");
412            }
413            builder.append("]");
414            return builder.toString();
415        }
416
417        /**
418         * Whether the supplied properties includes the specified property.
419         *
420         * @param properties A bit field of properties.
421         * @param property The property to check properties for.
422         * @return Whether the specified property is supported.
423         */
424        public static boolean hasProperty(int properties, int property) {
425            return (properties & property) == property;
426        }
427
428        /**
429         * Whether the properties of this {@code Details} includes the specified property.
430         *
431         * @param property The property to check properties for.
432         * @return Whether the specified property is supported.
433         */
434        public boolean hasProperty(int property) {
435            return hasProperty(mCallProperties, property);
436        }
437
438        /**
439         * Render a set of property bits ({@code PROPERTY_*}) as a human readable string.
440         *
441         * @param properties A property bit field.
442         * @return A human readable string representation.
443         */
444        public static String propertiesToString(int properties) {
445            StringBuilder builder = new StringBuilder();
446            builder.append("[Properties:");
447            if (hasProperty(properties, PROPERTY_CONFERENCE)) {
448                builder.append(" PROPERTY_CONFERENCE");
449            }
450            if (hasProperty(properties, PROPERTY_GENERIC_CONFERENCE)) {
451                builder.append(" PROPERTY_GENERIC_CONFERENCE");
452            }
453            if (hasProperty(properties, PROPERTY_WIFI)) {
454                builder.append(" PROPERTY_WIFI");
455            }
456            if (hasProperty(properties, PROPERTY_HIGH_DEF_AUDIO)) {
457                builder.append(" PROPERTY_HIGH_DEF_AUDIO");
458            }
459            if (hasProperty(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
460                builder.append(" PROPERTY_EMERGENCY_CALLBACK_MODE");
461            }
462            if (hasProperty(properties, PROPERTY_IS_EXTERNAL_CALL)) {
463                builder.append(" PROPERTY_IS_EXTERNAL_CALL");
464            }
465            builder.append("]");
466            return builder.toString();
467        }
468
469        /** {@hide} */
470        public String getTelecomCallId() {
471            return mTelecomCallId;
472        }
473
474        /**
475         * @return The handle (e.g., phone number) to which the {@code Call} is currently
476         * connected.
477         */
478        public Uri getHandle() {
479            return mHandle;
480        }
481
482        /**
483         * @return The presentation requirements for the handle. See
484         * {@link TelecomManager} for valid values.
485         */
486        public int getHandlePresentation() {
487            return mHandlePresentation;
488        }
489
490        /**
491         * @return The display name for the caller.
492         */
493        public String getCallerDisplayName() {
494            return mCallerDisplayName;
495        }
496
497        /**
498         * @return The presentation requirements for the caller display name. See
499         * {@link TelecomManager} for valid values.
500         */
501        public int getCallerDisplayNamePresentation() {
502            return mCallerDisplayNamePresentation;
503        }
504
505        /**
506         * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being
507         * routed.
508         */
509        public PhoneAccountHandle getAccountHandle() {
510            return mAccountHandle;
511        }
512
513        /**
514         * @return A bitmask of the capabilities of the {@code Call}, as defined by the various
515         *         {@code CAPABILITY_*} constants in this class.
516         */
517        public int getCallCapabilities() {
518            return mCallCapabilities;
519        }
520
521        /**
522         * @return A bitmask of the properties of the {@code Call}, as defined by the various
523         *         {@code PROPERTY_*} constants in this class.
524         */
525        public int getCallProperties() {
526            return mCallProperties;
527        }
528
529        /**
530         * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
531         * by {@link android.telecom.DisconnectCause}.
532         */
533        public DisconnectCause getDisconnectCause() {
534            return mDisconnectCause;
535        }
536
537        /**
538         * @return The time the {@code Call} has been connected. This information is updated
539         * periodically, but user interfaces should not rely on this to display any "call time
540         * clock".
541         */
542        public final long getConnectTimeMillis() {
543            return mConnectTimeMillis;
544        }
545
546        /**
547         * @return Information about any calling gateway the {@code Call} may be using.
548         */
549        public GatewayInfo getGatewayInfo() {
550            return mGatewayInfo;
551        }
552
553        /**
554         * @return The video state of the {@code Call}.
555         */
556        public int getVideoState() {
557            return mVideoState;
558        }
559
560        /**
561         * @return The current {@link android.telecom.StatusHints}, or {@code null} if none
562         * have been set.
563         */
564        public StatusHints getStatusHints() {
565            return mStatusHints;
566        }
567
568        /**
569         * @return The extras associated with this call.
570         */
571        public Bundle getExtras() {
572            return mExtras;
573        }
574
575        /**
576         * @return The extras used with the original intent to place this call.
577         */
578        public Bundle getIntentExtras() {
579            return mIntentExtras;
580        }
581
582        @Override
583        public boolean equals(Object o) {
584            if (o instanceof Details) {
585                Details d = (Details) o;
586                return
587                        Objects.equals(mHandle, d.mHandle) &&
588                        Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
589                        Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
590                        Objects.equals(mCallerDisplayNamePresentation,
591                                d.mCallerDisplayNamePresentation) &&
592                        Objects.equals(mAccountHandle, d.mAccountHandle) &&
593                        Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
594                        Objects.equals(mCallProperties, d.mCallProperties) &&
595                        Objects.equals(mDisconnectCause, d.mDisconnectCause) &&
596                        Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
597                        Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
598                        Objects.equals(mVideoState, d.mVideoState) &&
599                        Objects.equals(mStatusHints, d.mStatusHints) &&
600                        areBundlesEqual(mExtras, d.mExtras) &&
601                        areBundlesEqual(mIntentExtras, d.mIntentExtras);
602            }
603            return false;
604        }
605
606        @Override
607        public int hashCode() {
608            return
609                    Objects.hashCode(mHandle) +
610                    Objects.hashCode(mHandlePresentation) +
611                    Objects.hashCode(mCallerDisplayName) +
612                    Objects.hashCode(mCallerDisplayNamePresentation) +
613                    Objects.hashCode(mAccountHandle) +
614                    Objects.hashCode(mCallCapabilities) +
615                    Objects.hashCode(mCallProperties) +
616                    Objects.hashCode(mDisconnectCause) +
617                    Objects.hashCode(mConnectTimeMillis) +
618                    Objects.hashCode(mGatewayInfo) +
619                    Objects.hashCode(mVideoState) +
620                    Objects.hashCode(mStatusHints) +
621                    Objects.hashCode(mExtras) +
622                    Objects.hashCode(mIntentExtras);
623        }
624
625        /** {@hide} */
626        public Details(
627                String telecomCallId,
628                Uri handle,
629                int handlePresentation,
630                String callerDisplayName,
631                int callerDisplayNamePresentation,
632                PhoneAccountHandle accountHandle,
633                int capabilities,
634                int properties,
635                DisconnectCause disconnectCause,
636                long connectTimeMillis,
637                GatewayInfo gatewayInfo,
638                int videoState,
639                StatusHints statusHints,
640                Bundle extras,
641                Bundle intentExtras) {
642            mTelecomCallId = telecomCallId;
643            mHandle = handle;
644            mHandlePresentation = handlePresentation;
645            mCallerDisplayName = callerDisplayName;
646            mCallerDisplayNamePresentation = callerDisplayNamePresentation;
647            mAccountHandle = accountHandle;
648            mCallCapabilities = capabilities;
649            mCallProperties = properties;
650            mDisconnectCause = disconnectCause;
651            mConnectTimeMillis = connectTimeMillis;
652            mGatewayInfo = gatewayInfo;
653            mVideoState = videoState;
654            mStatusHints = statusHints;
655            mExtras = extras;
656            mIntentExtras = intentExtras;
657        }
658
659        /** {@hide} */
660        public static Details createFromParcelableCall(ParcelableCall parcelableCall) {
661            return new Details(
662                    parcelableCall.getId(),
663                    parcelableCall.getHandle(),
664                    parcelableCall.getHandlePresentation(),
665                    parcelableCall.getCallerDisplayName(),
666                    parcelableCall.getCallerDisplayNamePresentation(),
667                    parcelableCall.getAccountHandle(),
668                    parcelableCall.getCapabilities(),
669                    parcelableCall.getProperties(),
670                    parcelableCall.getDisconnectCause(),
671                    parcelableCall.getConnectTimeMillis(),
672                    parcelableCall.getGatewayInfo(),
673                    parcelableCall.getVideoState(),
674                    parcelableCall.getStatusHints(),
675                    parcelableCall.getExtras(),
676                    parcelableCall.getIntentExtras());
677        }
678
679        @Override
680        public String toString() {
681            StringBuilder sb = new StringBuilder();
682            sb.append("[pa: ");
683            sb.append(mAccountHandle);
684            sb.append(", hdl: ");
685            sb.append(Log.pii(mHandle));
686            sb.append(", caps: ");
687            sb.append(capabilitiesToString(mCallCapabilities));
688            sb.append(", props: ");
689            sb.append(mCallProperties);
690            sb.append("]");
691            return sb.toString();
692        }
693    }
694
695    public static abstract class Callback {
696        /**
697         * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
698         *
699         * @param call The {@code Call} invoking this method.
700         * @param state The new state of the {@code Call}.
701         */
702        public void onStateChanged(Call call, int state) {}
703
704        /**
705         * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
706         *
707         * @param call The {@code Call} invoking this method.
708         * @param parent The new parent of the {@code Call}.
709         */
710        public void onParentChanged(Call call, Call parent) {}
711
712        /**
713         * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
714         *
715         * @param call The {@code Call} invoking this method.
716         * @param children The new children of the {@code Call}.
717         */
718        public void onChildrenChanged(Call call, List<Call> children) {}
719
720        /**
721         * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
722         *
723         * @param call The {@code Call} invoking this method.
724         * @param details A {@code Details} object describing the {@code Call}.
725         */
726        public void onDetailsChanged(Call call, Details details) {}
727
728        /**
729         * Invoked when the text messages that can be used as responses to the incoming
730         * {@code Call} are loaded from the relevant database.
731         * See {@link #getCannedTextResponses()}.
732         *
733         * @param call The {@code Call} invoking this method.
734         * @param cannedTextResponses The text messages useable as responses.
735         */
736        public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
737
738        /**
739         * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
740         * character. This causes the post-dial signals to stop pending user confirmation. An
741         * implementation should present this choice to the user and invoke
742         * {@link #postDialContinue(boolean)} when the user makes the choice.
743         *
744         * @param call The {@code Call} invoking this method.
745         * @param remainingPostDialSequence The post-dial characters that remain to be sent.
746         */
747        public void onPostDialWait(Call call, String remainingPostDialSequence) {}
748
749        /**
750         * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed.
751         *
752         * @param call The {@code Call} invoking this method.
753         * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
754         */
755        public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
756
757        /**
758         * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
759         * up their UI for the {@code Call} in response to state transitions. Specifically,
760         * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
761         * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
762         * clients should wait for this method to be invoked.
763         *
764         * @param call The {@code Call} being destroyed.
765         */
766        public void onCallDestroyed(Call call) {}
767
768        /**
769         * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be
770         * conferenced.
771         *
772         * @param call The {@code Call} being updated.
773         * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be
774         *          conferenced.
775         */
776        public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
777
778        /**
779         * Invoked when a call receives an event from its associated {@link Connection}.
780         * <p>
781         * See {@link Connection#sendConnectionEvent(String, Bundle)}.
782         *
783         * @param call The {@code Call} receiving the event.
784         * @param event The event.
785         * @param extras Extras associated with the connection event.
786         */
787        public void onConnectionEvent(Call call, String event, Bundle extras) {}
788    }
789
790    /**
791     * @deprecated Use {@code Call.Callback} instead.
792     * @hide
793     */
794    @Deprecated
795    @SystemApi
796    public static abstract class Listener extends Callback { }
797
798    private final Phone mPhone;
799    private final String mTelecomCallId;
800    private final InCallAdapter mInCallAdapter;
801    private final List<String> mChildrenIds = new ArrayList<>();
802    private final List<Call> mChildren = new ArrayList<>();
803    private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
804    private final List<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArrayList<>();
805    private final List<Call> mConferenceableCalls = new ArrayList<>();
806    private final List<Call> mUnmodifiableConferenceableCalls =
807            Collections.unmodifiableList(mConferenceableCalls);
808
809    private boolean mChildrenCached;
810    private String mParentId = null;
811    private int mState;
812    private List<String> mCannedTextResponses = null;
813    private String mRemainingPostDialSequence;
814    private VideoCallImpl mVideoCallImpl;
815    private Details mDetails;
816
817    /**
818     * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
819     *
820     * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
821     * remaining or this {@code Call} is not in a post-dial state.
822     */
823    public String getRemainingPostDialSequence() {
824        return mRemainingPostDialSequence;
825    }
826
827    /**
828     * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
829     * @param videoState The video state in which to answer the call.
830     */
831    public void answer(int videoState) {
832        mInCallAdapter.answerCall(mTelecomCallId, videoState);
833    }
834
835    /**
836     * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
837     *
838     * @param rejectWithMessage Whether to reject with a text message.
839     * @param textMessage An optional text message with which to respond.
840     */
841    public void reject(boolean rejectWithMessage, String textMessage) {
842        mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage);
843    }
844
845    /**
846     * Instructs this {@code Call} to disconnect.
847     */
848    public void disconnect() {
849        mInCallAdapter.disconnectCall(mTelecomCallId);
850    }
851
852    /**
853     * Instructs this {@code Call} to go on hold.
854     */
855    public void hold() {
856        mInCallAdapter.holdCall(mTelecomCallId);
857    }
858
859    /**
860     * Instructs this {@link #STATE_HOLDING} call to release from hold.
861     */
862    public void unhold() {
863        mInCallAdapter.unholdCall(mTelecomCallId);
864    }
865
866    /**
867     * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
868     *
869     * Any other currently playing DTMF tone in the specified call is immediately stopped.
870     *
871     * @param digit A character representing the DTMF digit for which to play the tone. This
872     *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
873     */
874    public void playDtmfTone(char digit) {
875        mInCallAdapter.playDtmfTone(mTelecomCallId, digit);
876    }
877
878    /**
879     * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
880     * currently playing.
881     *
882     * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
883     * currently playing, this method will do nothing.
884     */
885    public void stopDtmfTone() {
886        mInCallAdapter.stopDtmfTone(mTelecomCallId);
887    }
888
889    /**
890     * Instructs this {@code Call} to continue playing a post-dial DTMF string.
891     *
892     * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
893     * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
894     *
895     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
896     * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
897     *
898     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
899     * {@code Call} will pause playing the tones and notify callbacks via
900     * {@link Callback#onPostDialWait(Call, String)}. At this point, the in-call app
901     * should display to the user an indication of this state and an affordance to continue
902     * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
903     * app should invoke the {@link #postDialContinue(boolean)} method.
904     *
905     * @param proceed Whether or not to continue with the post-dial sequence.
906     */
907    public void postDialContinue(boolean proceed) {
908        mInCallAdapter.postDialContinue(mTelecomCallId, proceed);
909    }
910
911    /**
912     * Notifies this {@code Call} that an account has been selected and to proceed with placing
913     * an outgoing call. Optionally sets this account as the default account.
914     */
915    public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
916        mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle, setDefault);
917
918    }
919
920    /**
921     * Instructs this {@code Call} to enter a conference.
922     *
923     * @param callToConferenceWith The other call with which to conference.
924     */
925    public void conference(Call callToConferenceWith) {
926        if (callToConferenceWith != null) {
927            mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId);
928        }
929    }
930
931    /**
932     * Instructs this {@code Call} to split from any conference call with which it may be
933     * connected.
934     */
935    public void splitFromConference() {
936        mInCallAdapter.splitFromConference(mTelecomCallId);
937    }
938
939    /**
940     * Merges the calls within this conference. See {@link Details#CAPABILITY_MERGE_CONFERENCE}.
941     */
942    public void mergeConference() {
943        mInCallAdapter.mergeConference(mTelecomCallId);
944    }
945
946    /**
947     * Swaps the calls within this conference. See {@link Details#CAPABILITY_SWAP_CONFERENCE}.
948     */
949    public void swapConference() {
950        mInCallAdapter.swapConference(mTelecomCallId);
951    }
952
953    /**
954     * Initiates a request to the {@link ConnectionService} to pull an external call to the local
955     * device.
956     * <p>
957     * Calls to this method are ignored if the call does not have the
958     * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} property set.
959     * <p>
960     * An {@link InCallService} will only see calls which support this method if it has the
961     * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
962     * in its manifest.
963     */
964    public void pullExternalCall() {
965        // If this isn't an external call, ignore the request.
966        if (!mDetails.hasProperty(Details.PROPERTY_IS_EXTERNAL_CALL)) {
967            return;
968        }
969
970        mInCallAdapter.pullExternalCall(mTelecomCallId);
971    }
972
973    /**
974     * Sends a {@code Call} event from this {@code Call} to the associated {@link Connection} in
975     * the {@link ConnectionService}.
976     * <p>
977     * Events are exposed to {@link ConnectionService} implementations via
978     * {@link android.telecom.Connection#onCallEvent(String, Bundle)}.
979     * <p>
980     * No assumptions should be made as to how a {@link ConnectionService} will handle these events.
981     * Events should be fully qualified (e.g., com.example.event.MY_EVENT) to avoid conflicts.
982     *
983     * @param event The connection event.
984     * @param extras Bundle containing extra information associated with the event.
985     */
986    public void sendCallEvent(String event, Bundle extras) {
987        mInCallAdapter.sendCallEvent(mTelecomCallId, event, extras);
988    }
989
990    /**
991     * Obtains the parent of this {@code Call} in a conference, if any.
992     *
993     * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
994     * child of any conference {@code Call}s.
995     */
996    public Call getParent() {
997        if (mParentId != null) {
998            return mPhone.internalGetCallByTelecomId(mParentId);
999        }
1000        return null;
1001    }
1002
1003    /**
1004     * Obtains the children of this conference {@code Call}, if any.
1005     *
1006     * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
1007     * {@code List} otherwise.
1008     */
1009    public List<Call> getChildren() {
1010        if (!mChildrenCached) {
1011            mChildrenCached = true;
1012            mChildren.clear();
1013
1014            for(String id : mChildrenIds) {
1015                Call call = mPhone.internalGetCallByTelecomId(id);
1016                if (call == null) {
1017                    // At least one child was still not found, so do not save true for "cached"
1018                    mChildrenCached = false;
1019                } else {
1020                    mChildren.add(call);
1021                }
1022            }
1023        }
1024
1025        return mUnmodifiableChildren;
1026    }
1027
1028    /**
1029     * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference.
1030     *
1031     * @return The list of conferenceable {@code Call}s.
1032     */
1033    public List<Call> getConferenceableCalls() {
1034        return mUnmodifiableConferenceableCalls;
1035    }
1036
1037    /**
1038     * Obtains the state of this {@code Call}.
1039     *
1040     * @return A state value, chosen from the {@code STATE_*} constants.
1041     */
1042    public int getState() {
1043        return mState;
1044    }
1045
1046    /**
1047     * Obtains a list of canned, pre-configured message responses to present to the user as
1048     * ways of rejecting this {@code Call} using via a text message.
1049     *
1050     * @see #reject(boolean, String)
1051     *
1052     * @return A list of canned text message responses.
1053     */
1054    public List<String> getCannedTextResponses() {
1055        return mCannedTextResponses;
1056    }
1057
1058    /**
1059     * Obtains an object that can be used to display video from this {@code Call}.
1060     *
1061     * @return An {@code Call.VideoCall}.
1062     */
1063    public InCallService.VideoCall getVideoCall() {
1064        return mVideoCallImpl;
1065    }
1066
1067    /**
1068     * Obtains an object containing call details.
1069     *
1070     * @return A {@link Details} object. Depending on the state of the {@code Call}, the
1071     * result may be {@code null}.
1072     */
1073    public Details getDetails() {
1074        return mDetails;
1075    }
1076
1077    /**
1078     * Registers a callback to this {@code Call}.
1079     *
1080     * @param callback A {@code Callback}.
1081     */
1082    public void registerCallback(Callback callback) {
1083        registerCallback(callback, new Handler());
1084    }
1085
1086    /**
1087     * Registers a callback to this {@code Call}.
1088     *
1089     * @param callback A {@code Callback}.
1090     * @param handler A handler which command and status changes will be delivered to.
1091     */
1092    public void registerCallback(Callback callback, Handler handler) {
1093        unregisterCallback(callback);
1094        // Don't allow new callback registration if the call is already being destroyed.
1095        if (callback != null && handler != null && mState != STATE_DISCONNECTED) {
1096            mCallbackRecords.add(new CallbackRecord<Callback>(callback, handler));
1097        }
1098    }
1099
1100    /**
1101     * Unregisters a callback from this {@code Call}.
1102     *
1103     * @param callback A {@code Callback}.
1104     */
1105    public void unregisterCallback(Callback callback) {
1106        // Don't allow callback deregistration if the call is already being destroyed.
1107        if (callback != null && mState != STATE_DISCONNECTED) {
1108            for (CallbackRecord<Callback> record : mCallbackRecords) {
1109                if (record.getCallback() == callback) {
1110                    mCallbackRecords.remove(record);
1111                    break;
1112                }
1113            }
1114        }
1115    }
1116
1117    @Override
1118    public String toString() {
1119        return new StringBuilder().
1120                append("Call [id: ").
1121                append(mTelecomCallId).
1122                append(", state: ").
1123                append(stateToString(mState)).
1124                append(", details: ").
1125                append(mDetails).
1126                append("]").toString();
1127    }
1128
1129    /**
1130     * @param state An integer value of a {@code STATE_*} constant.
1131     * @return A string representation of the value.
1132     */
1133    private static String stateToString(int state) {
1134        switch (state) {
1135            case STATE_NEW:
1136                return "NEW";
1137            case STATE_RINGING:
1138                return "RINGING";
1139            case STATE_DIALING:
1140                return "DIALING";
1141            case STATE_ACTIVE:
1142                return "ACTIVE";
1143            case STATE_HOLDING:
1144                return "HOLDING";
1145            case STATE_DISCONNECTED:
1146                return "DISCONNECTED";
1147            case STATE_CONNECTING:
1148                return "CONNECTING";
1149            case STATE_DISCONNECTING:
1150                return "DISCONNECTING";
1151            case STATE_SELECT_PHONE_ACCOUNT:
1152                return "SELECT_PHONE_ACCOUNT";
1153            default:
1154                Log.w(Call.class, "Unknown state %d", state);
1155                return "UNKNOWN";
1156        }
1157    }
1158
1159    /**
1160     * Adds a listener to this {@code Call}.
1161     *
1162     * @param listener A {@code Listener}.
1163     * @deprecated Use {@link #registerCallback} instead.
1164     * @hide
1165     */
1166    @Deprecated
1167    @SystemApi
1168    public void addListener(Listener listener) {
1169        registerCallback(listener);
1170    }
1171
1172    /**
1173     * Removes a listener from this {@code Call}.
1174     *
1175     * @param listener A {@code Listener}.
1176     * @deprecated Use {@link #unregisterCallback} instead.
1177     * @hide
1178     */
1179    @Deprecated
1180    @SystemApi
1181    public void removeListener(Listener listener) {
1182        unregisterCallback(listener);
1183    }
1184
1185    /** {@hide} */
1186    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
1187        mPhone = phone;
1188        mTelecomCallId = telecomCallId;
1189        mInCallAdapter = inCallAdapter;
1190        mState = STATE_NEW;
1191    }
1192
1193    /** {@hide} */
1194    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state) {
1195        mPhone = phone;
1196        mTelecomCallId = telecomCallId;
1197        mInCallAdapter = inCallAdapter;
1198        mState = state;
1199    }
1200
1201    /** {@hide} */
1202    final String internalGetCallId() {
1203        return mTelecomCallId;
1204    }
1205
1206    /** {@hide} */
1207    final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
1208        // First, we update the internal state as far as possible before firing any updates.
1209        Details details = Details.createFromParcelableCall(parcelableCall);
1210        boolean detailsChanged = !Objects.equals(mDetails, details);
1211        if (detailsChanged) {
1212            mDetails = details;
1213        }
1214
1215        boolean cannedTextResponsesChanged = false;
1216        if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
1217                && !parcelableCall.getCannedSmsResponses().isEmpty()) {
1218            mCannedTextResponses =
1219                    Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
1220            cannedTextResponsesChanged = true;
1221        }
1222
1223        VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl();
1224        boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
1225                !Objects.equals(mVideoCallImpl, newVideoCallImpl);
1226        if (videoCallChanged) {
1227            mVideoCallImpl = newVideoCallImpl;
1228        }
1229        if (mVideoCallImpl != null) {
1230            mVideoCallImpl.setVideoState(getDetails().getVideoState());
1231        }
1232
1233        int state = parcelableCall.getState();
1234        boolean stateChanged = mState != state;
1235        if (stateChanged) {
1236            mState = state;
1237        }
1238
1239        String parentId = parcelableCall.getParentCallId();
1240        boolean parentChanged = !Objects.equals(mParentId, parentId);
1241        if (parentChanged) {
1242            mParentId = parentId;
1243        }
1244
1245        List<String> childCallIds = parcelableCall.getChildCallIds();
1246        boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds);
1247        if (childrenChanged) {
1248            mChildrenIds.clear();
1249            mChildrenIds.addAll(parcelableCall.getChildCallIds());
1250            mChildrenCached = false;
1251        }
1252
1253        List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds();
1254        List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size());
1255        for (String otherId : conferenceableCallIds) {
1256            if (callIdMap.containsKey(otherId)) {
1257                conferenceableCalls.add(callIdMap.get(otherId));
1258            }
1259        }
1260
1261        if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) {
1262            mConferenceableCalls.clear();
1263            mConferenceableCalls.addAll(conferenceableCalls);
1264            fireConferenceableCallsChanged();
1265        }
1266
1267        // Now we fire updates, ensuring that any client who listens to any of these notifications
1268        // gets the most up-to-date state.
1269
1270        if (stateChanged) {
1271            fireStateChanged(mState);
1272        }
1273        if (detailsChanged) {
1274            fireDetailsChanged(mDetails);
1275        }
1276        if (cannedTextResponsesChanged) {
1277            fireCannedTextResponsesLoaded(mCannedTextResponses);
1278        }
1279        if (videoCallChanged) {
1280            fireVideoCallChanged(mVideoCallImpl);
1281        }
1282        if (parentChanged) {
1283            fireParentChanged(getParent());
1284        }
1285        if (childrenChanged) {
1286            fireChildrenChanged(getChildren());
1287        }
1288
1289        // If we have transitioned to DISCONNECTED, that means we need to notify clients and
1290        // remove ourselves from the Phone. Note that we do this after completing all state updates
1291        // so a client can cleanly transition all their UI to the state appropriate for a
1292        // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
1293        if (mState == STATE_DISCONNECTED) {
1294            fireCallDestroyed();
1295        }
1296    }
1297
1298    /** {@hide} */
1299    final void internalSetPostDialWait(String remaining) {
1300        mRemainingPostDialSequence = remaining;
1301        firePostDialWait(mRemainingPostDialSequence);
1302    }
1303
1304    /** {@hide} */
1305    final void internalSetDisconnected() {
1306        if (mState != Call.STATE_DISCONNECTED) {
1307            mState = Call.STATE_DISCONNECTED;
1308            fireStateChanged(mState);
1309            fireCallDestroyed();
1310        }
1311    }
1312
1313    /** {@hide} */
1314    final void internalOnConnectionEvent(String event, Bundle extras) {
1315        fireOnConnectionEvent(event, extras);
1316    }
1317
1318    private void fireStateChanged(final int newState) {
1319        for (CallbackRecord<Callback> record : mCallbackRecords) {
1320            final Call call = this;
1321            final Callback callback = record.getCallback();
1322            record.getHandler().post(new Runnable() {
1323                @Override
1324                public void run() {
1325                    callback.onStateChanged(call, newState);
1326                }
1327            });
1328        }
1329    }
1330
1331    private void fireParentChanged(final Call newParent) {
1332        for (CallbackRecord<Callback> record : mCallbackRecords) {
1333            final Call call = this;
1334            final Callback callback = record.getCallback();
1335            record.getHandler().post(new Runnable() {
1336                @Override
1337                public void run() {
1338                    callback.onParentChanged(call, newParent);
1339                }
1340            });
1341        }
1342    }
1343
1344    private void fireChildrenChanged(final List<Call> children) {
1345        for (CallbackRecord<Callback> record : mCallbackRecords) {
1346            final Call call = this;
1347            final Callback callback = record.getCallback();
1348            record.getHandler().post(new Runnable() {
1349                @Override
1350                public void run() {
1351                    callback.onChildrenChanged(call, children);
1352                }
1353            });
1354        }
1355    }
1356
1357    private void fireDetailsChanged(final Details details) {
1358        for (CallbackRecord<Callback> record : mCallbackRecords) {
1359            final Call call = this;
1360            final Callback callback = record.getCallback();
1361            record.getHandler().post(new Runnable() {
1362                @Override
1363                public void run() {
1364                    callback.onDetailsChanged(call, details);
1365                }
1366            });
1367        }
1368    }
1369
1370    private void fireCannedTextResponsesLoaded(final List<String> cannedTextResponses) {
1371        for (CallbackRecord<Callback> record : mCallbackRecords) {
1372            final Call call = this;
1373            final Callback callback = record.getCallback();
1374            record.getHandler().post(new Runnable() {
1375                @Override
1376                public void run() {
1377                    callback.onCannedTextResponsesLoaded(call, cannedTextResponses);
1378                }
1379            });
1380        }
1381    }
1382
1383    private void fireVideoCallChanged(final InCallService.VideoCall videoCall) {
1384        for (CallbackRecord<Callback> record : mCallbackRecords) {
1385            final Call call = this;
1386            final Callback callback = record.getCallback();
1387            record.getHandler().post(new Runnable() {
1388                @Override
1389                public void run() {
1390                    callback.onVideoCallChanged(call, videoCall);
1391                }
1392            });
1393        }
1394    }
1395
1396    private void firePostDialWait(final String remainingPostDialSequence) {
1397        for (CallbackRecord<Callback> record : mCallbackRecords) {
1398            final Call call = this;
1399            final Callback callback = record.getCallback();
1400            record.getHandler().post(new Runnable() {
1401                @Override
1402                public void run() {
1403                    callback.onPostDialWait(call, remainingPostDialSequence);
1404                }
1405            });
1406        }
1407    }
1408
1409    private void fireCallDestroyed() {
1410        /**
1411         * To preserve the ordering of the Call's onCallDestroyed callback and Phone's
1412         * onCallRemoved callback, we remove this call from the Phone's record
1413         * only once all of the registered onCallDestroyed callbacks are executed.
1414         * All the callbacks get removed from our records as a part of this operation
1415         * since onCallDestroyed is the final callback.
1416         */
1417        final Call call = this;
1418        if (mCallbackRecords.isEmpty()) {
1419            // No callbacks registered, remove the call from Phone's record.
1420            mPhone.internalRemoveCall(call);
1421        }
1422        for (final CallbackRecord<Callback> record : mCallbackRecords) {
1423            final Callback callback = record.getCallback();
1424            record.getHandler().post(new Runnable() {
1425                @Override
1426                public void run() {
1427                    boolean isFinalRemoval = false;
1428                    RuntimeException toThrow = null;
1429                    try {
1430                        callback.onCallDestroyed(call);
1431                    } catch (RuntimeException e) {
1432                            toThrow = e;
1433                    }
1434                    synchronized(Call.this) {
1435                        mCallbackRecords.remove(record);
1436                        if (mCallbackRecords.isEmpty()) {
1437                            isFinalRemoval = true;
1438                        }
1439                    }
1440                    if (isFinalRemoval) {
1441                        mPhone.internalRemoveCall(call);
1442                    }
1443                    if (toThrow != null) {
1444                        throw toThrow;
1445                    }
1446                }
1447            });
1448        }
1449    }
1450
1451    private void fireConferenceableCallsChanged() {
1452        for (CallbackRecord<Callback> record : mCallbackRecords) {
1453            final Call call = this;
1454            final Callback callback = record.getCallback();
1455            record.getHandler().post(new Runnable() {
1456                @Override
1457                public void run() {
1458                    callback.onConferenceableCallsChanged(call, mUnmodifiableConferenceableCalls);
1459                }
1460            });
1461        }
1462    }
1463
1464    /**
1465     * Notifies listeners of an incoming connection event.
1466     * <p>
1467     * Connection events are issued via {@link Connection#sendConnectionEvent(String, Bundle)}.
1468     *
1469     * @param event
1470     * @param extras
1471     */
1472    private void fireOnConnectionEvent(final String event, final Bundle extras) {
1473        for (CallbackRecord<Callback> record : mCallbackRecords) {
1474            final Call call = this;
1475            final Callback callback = record.getCallback();
1476            record.getHandler().post(new Runnable() {
1477                @Override
1478                public void run() {
1479                    callback.onConnectionEvent(call, event, extras);
1480                }
1481            });
1482        }
1483    }
1484
1485    /**
1486     * Determines if two bundles are equal.
1487     *
1488     * @param bundle The original bundle.
1489     * @param newBundle The bundle to compare with.
1490     * @retrun {@code true} if the bundles are equal, {@code false} otherwise.
1491     */
1492    private static boolean areBundlesEqual(Bundle bundle, Bundle newBundle) {
1493        if (bundle == null || newBundle == null) {
1494            return bundle == newBundle;
1495        }
1496
1497        if (bundle.size() != newBundle.size()) {
1498            return false;
1499        }
1500
1501        for(String key : bundle.keySet()) {
1502            if (key != null) {
1503                final Object value = bundle.get(key);
1504                final Object newValue = newBundle.get(key);
1505                if (!Objects.equals(value, newValue)) {
1506                    return false;
1507                }
1508            }
1509        }
1510        return true;
1511    }
1512}
1513