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.IntDef;
20import android.annotation.Nullable;
21import android.annotation.SystemApi;
22import android.net.Uri;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.ParcelFileDescriptor;
26
27import java.io.IOException;
28import java.io.InputStreamReader;
29import java.io.OutputStreamWriter;
30import java.lang.String;
31import java.lang.annotation.Retention;
32import java.lang.annotation.RetentionPolicy;
33import java.nio.charset.StandardCharsets;
34import java.util.ArrayList;
35import java.util.Arrays;
36import java.util.Collections;
37import java.util.List;
38import java.util.Map;
39import java.util.Objects;
40import java.util.concurrent.CopyOnWriteArrayList;
41
42/**
43 * Represents an ongoing phone call that the in-call app should present to the user.
44 */
45public final class Call {
46    /**
47     * The state of a {@code Call} when newly created.
48     */
49    public static final int STATE_NEW = 0;
50
51    /**
52     * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected.
53     */
54    public static final int STATE_DIALING = 1;
55
56    /**
57     * The state of an incoming {@code Call} when ringing locally, but not yet connected.
58     */
59    public static final int STATE_RINGING = 2;
60
61    /**
62     * The state of a {@code Call} when in a holding state.
63     */
64    public static final int STATE_HOLDING = 3;
65
66    /**
67     * The state of a {@code Call} when actively supporting conversation.
68     */
69    public static final int STATE_ACTIVE = 4;
70
71    /**
72     * The state of a {@code Call} when no further voice or other communication is being
73     * transmitted, the remote side has been or will inevitably be informed that the {@code Call}
74     * is no longer active, and the local data transport has or inevitably will release resources
75     * associated with this {@code Call}.
76     */
77    public static final int STATE_DISCONNECTED = 7;
78
79    /**
80     * The state of an outgoing {@code Call} when waiting on user to select a
81     * {@link PhoneAccount} through which to place the call.
82     */
83    public static final int STATE_SELECT_PHONE_ACCOUNT = 8;
84
85    /**
86     * @hide
87     * @deprecated use STATE_SELECT_PHONE_ACCOUNT.
88     */
89    @Deprecated
90    @SystemApi
91    public static final int STATE_PRE_DIAL_WAIT = STATE_SELECT_PHONE_ACCOUNT;
92
93    /**
94     * The initial state of an outgoing {@code Call}.
95     * Common transitions are to {@link #STATE_DIALING} state for a successful call or
96     * {@link #STATE_DISCONNECTED} if it failed.
97     */
98    public static final int STATE_CONNECTING = 9;
99
100    /**
101     * The state of a {@code Call} when the user has initiated a disconnection of the call, but the
102     * call has not yet been disconnected by the underlying {@code ConnectionService}.  The next
103     * state of the call is (potentially) {@link #STATE_DISCONNECTED}.
104     */
105    public static final int STATE_DISCONNECTING = 10;
106
107    /**
108     * The state of an external call which is in the process of being pulled from a remote device to
109     * the local device.
110     * <p>
111     * A call can only be in this state if the {@link Details#PROPERTY_IS_EXTERNAL_CALL} property
112     * and {@link Details#CAPABILITY_CAN_PULL_CALL} capability are set on the call.
113     * <p>
114     * An {@link InCallService} will only see this state if it has the
115     * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
116     * manifest.
117     */
118    public static final int STATE_PULLING_CALL = 11;
119
120    /**
121     * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
122     * extras. Used to pass the phone accounts to display on the front end to the user in order to
123     * select phone accounts to (for example) place a call.
124     */
125    public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
126
127    /**
128     * Extra key used to indicate the time (in milliseconds since midnight, January 1, 1970 UTC)
129     * when the last outgoing emergency call was made.  This is used to identify potential emergency
130     * callbacks.
131     */
132    public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS =
133            "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
134
135    public static class Details {
136
137        /** Call can currently be put on hold or unheld. */
138        public static final int CAPABILITY_HOLD = 0x00000001;
139
140        /** Call supports the hold feature. */
141        public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
142
143        /**
144         * Calls within a conference can be merged. A {@link ConnectionService} has the option to
145         * add a {@link Conference} call before the child {@link Connection}s are merged. This is how
146         * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
147         * capability allows a merge button to be shown while the conference call is in the foreground
148         * of the in-call UI.
149         * <p>
150         * This is only intended for use by a {@link Conference}.
151         */
152        public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
153
154        /**
155         * Calls within a conference can be swapped between foreground and background.
156         * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
157         * <p>
158         * This is only intended for use by a {@link Conference}.
159         */
160        public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
161
162        /**
163         * @hide
164         */
165        public static final int CAPABILITY_UNUSED_1 = 0x00000010;
166
167        /** Call supports responding via text option. */
168        public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
169
170        /** Call can be muted. */
171        public static final int CAPABILITY_MUTE = 0x00000040;
172
173        /**
174         * Call supports conference call management. This capability only applies to {@link Conference}
175         * calls which can have {@link Connection}s as children.
176         */
177        public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
178
179        /**
180         * Local device supports receiving video.
181         */
182        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
183
184        /**
185         * Local device supports transmitting video.
186         */
187        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
188
189        /**
190         * Local device supports bidirectional video calling.
191         */
192        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
193                CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
194
195        /**
196         * Remote device supports receiving video.
197         */
198        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
199
200        /**
201         * Remote device supports transmitting video.
202         */
203        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
204
205        /**
206         * Remote device supports bidirectional video calling.
207         */
208        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
209                CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
210
211        /**
212         * Call is able to be separated from its parent {@code Conference}, if any.
213         */
214        public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
215
216        /**
217         * Call is able to be individually disconnected when in a {@code Conference}.
218         */
219        public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
220
221        /**
222         * Speed up audio setup for MT call.
223         * @hide
224         */
225        public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
226
227        /**
228         * Call can be upgraded to a video call.
229         * @hide
230         */
231        public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
232
233        /**
234         * For video calls, indicates whether the outgoing video for the call can be paused using
235         * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
236         */
237        public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
238
239        /**
240         * Call sends responses through connection.
241         * @hide
242         */
243        public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00200000;
244
245        /**
246         * When set, prevents a video {@code Call} from being downgraded to an audio-only call.
247         * <p>
248         * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
249         * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
250         * downgraded from a video call back to a VideoState of
251         * {@link VideoProfile#STATE_AUDIO_ONLY}.
252         * <p>
253         * Intuitively, a call which can be downgraded to audio should also have local and remote
254         * video
255         * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
256         * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
257         */
258        public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000;
259
260        /**
261         * When set for an external call, indicates that this {@code Call} can be pulled from a
262         * remote device to the current device.
263         * <p>
264         * Should only be set on a {@code Call} where {@link #PROPERTY_IS_EXTERNAL_CALL} is set.
265         * <p>
266         * An {@link InCallService} will only see calls with this capability if it has the
267         * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
268         * in its manifest.
269         * <p>
270         * See {@link Connection#CAPABILITY_CAN_PULL_CALL} and
271         * {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
272         */
273        public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
274
275        //******************************************************************************************
276        // Next CAPABILITY value: 0x01000000
277        //******************************************************************************************
278
279        /**
280         * Whether the call is currently a conference.
281         */
282        public static final int PROPERTY_CONFERENCE = 0x00000001;
283
284        /**
285         * Whether the call is a generic conference, where we do not know the precise state of
286         * participants in the conference (eg. on CDMA).
287         */
288        public static final int PROPERTY_GENERIC_CONFERENCE = 0x00000002;
289
290        /**
291         * Whether the call is made while the device is in emergency callback mode.
292         */
293        public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 0x00000004;
294
295        /**
296         * Connection is using WIFI.
297         */
298        public static final int PROPERTY_WIFI = 0x00000008;
299
300        /**
301         * Call is using high definition audio.
302         */
303        public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010;
304
305        /**
306         * Whether the call is associated with the work profile.
307         */
308        public static final int PROPERTY_ENTERPRISE_CALL = 0x00000020;
309
310        /**
311         * When set, indicates that this {@code Call} does not actually exist locally for the
312         * {@link ConnectionService}.
313         * <p>
314         * Consider, for example, a scenario where a user has two phones with the same phone number.
315         * When a user places a call on one device, the telephony stack can represent that call on
316         * the other device by adding it to the {@link ConnectionService} with the
317         * {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
318         * <p>
319         * An {@link InCallService} will only see calls with this property if it has the
320         * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
321         * in its manifest.
322         * <p>
323         * See {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
324         */
325        public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040;
326
327        /**
328         * Indicates that the call has CDMA Enhanced Voice Privacy enabled.
329         */
330        public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 0x00000080;
331
332        /**
333         * Indicates that the call is from a self-managed {@link ConnectionService}.
334         * <p>
335         * See also {@link Connection#PROPERTY_SELF_MANAGED}
336         */
337        public static final int PROPERTY_SELF_MANAGED = 0x00000100;
338
339        //******************************************************************************************
340        // Next PROPERTY value: 0x00000200
341        //******************************************************************************************
342
343        private final String mTelecomCallId;
344        private final Uri mHandle;
345        private final int mHandlePresentation;
346        private final String mCallerDisplayName;
347        private final int mCallerDisplayNamePresentation;
348        private final PhoneAccountHandle mAccountHandle;
349        private final int mCallCapabilities;
350        private final int mCallProperties;
351        private final int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL;
352        private final DisconnectCause mDisconnectCause;
353        private final long mConnectTimeMillis;
354        private final GatewayInfo mGatewayInfo;
355        private final int mVideoState;
356        private final StatusHints mStatusHints;
357        private final Bundle mExtras;
358        private final Bundle mIntentExtras;
359        private final long mCreationTimeMillis;
360
361        /**
362         * Whether the supplied capabilities  supports the specified capability.
363         *
364         * @param capabilities A bit field of capabilities.
365         * @param capability The capability to check capabilities for.
366         * @return Whether the specified capability is supported.
367         */
368        public static boolean can(int capabilities, int capability) {
369            return (capabilities & capability) == capability;
370        }
371
372        /**
373         * Whether the capabilities of this {@code Details} supports the specified capability.
374         *
375         * @param capability The capability to check capabilities for.
376         * @return Whether the specified capability is supported.
377         */
378        public boolean can(int capability) {
379            return can(mCallCapabilities, capability);
380        }
381
382        /**
383         * Render a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
384         *
385         * @param capabilities A capability bit field.
386         * @return A human readable string representation.
387         */
388        public static String capabilitiesToString(int capabilities) {
389            StringBuilder builder = new StringBuilder();
390            builder.append("[Capabilities:");
391            if (can(capabilities, CAPABILITY_HOLD)) {
392                builder.append(" CAPABILITY_HOLD");
393            }
394            if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
395                builder.append(" CAPABILITY_SUPPORT_HOLD");
396            }
397            if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
398                builder.append(" CAPABILITY_MERGE_CONFERENCE");
399            }
400            if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
401                builder.append(" CAPABILITY_SWAP_CONFERENCE");
402            }
403            if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
404                builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
405            }
406            if (can(capabilities, CAPABILITY_MUTE)) {
407                builder.append(" CAPABILITY_MUTE");
408            }
409            if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
410                builder.append(" CAPABILITY_MANAGE_CONFERENCE");
411            }
412            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
413                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
414            }
415            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
416                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
417            }
418            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
419                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
420            }
421            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
422                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
423            }
424            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
425                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
426            }
427            if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
428                builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
429            }
430            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
431                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
432            }
433            if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
434                builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
435            }
436            if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
437                builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
438            }
439            if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
440                builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
441            }
442            if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
443                builder.append(" CAPABILITY_CAN_PULL_CALL");
444            }
445            builder.append("]");
446            return builder.toString();
447        }
448
449        /**
450         * Whether the supplied properties includes the specified property.
451         *
452         * @param properties A bit field of properties.
453         * @param property The property to check properties for.
454         * @return Whether the specified property is supported.
455         */
456        public static boolean hasProperty(int properties, int property) {
457            return (properties & property) == property;
458        }
459
460        /**
461         * Whether the properties of this {@code Details} includes the specified property.
462         *
463         * @param property The property to check properties for.
464         * @return Whether the specified property is supported.
465         */
466        public boolean hasProperty(int property) {
467            return hasProperty(mCallProperties, property);
468        }
469
470        /**
471         * Render a set of property bits ({@code PROPERTY_*}) as a human readable string.
472         *
473         * @param properties A property bit field.
474         * @return A human readable string representation.
475         */
476        public static String propertiesToString(int properties) {
477            StringBuilder builder = new StringBuilder();
478            builder.append("[Properties:");
479            if (hasProperty(properties, PROPERTY_CONFERENCE)) {
480                builder.append(" PROPERTY_CONFERENCE");
481            }
482            if (hasProperty(properties, PROPERTY_GENERIC_CONFERENCE)) {
483                builder.append(" PROPERTY_GENERIC_CONFERENCE");
484            }
485            if (hasProperty(properties, PROPERTY_WIFI)) {
486                builder.append(" PROPERTY_WIFI");
487            }
488            if (hasProperty(properties, PROPERTY_HIGH_DEF_AUDIO)) {
489                builder.append(" PROPERTY_HIGH_DEF_AUDIO");
490            }
491            if (hasProperty(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
492                builder.append(" PROPERTY_EMERGENCY_CALLBACK_MODE");
493            }
494            if (hasProperty(properties, PROPERTY_IS_EXTERNAL_CALL)) {
495                builder.append(" PROPERTY_IS_EXTERNAL_CALL");
496            }
497            if(hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
498                builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY");
499            }
500            builder.append("]");
501            return builder.toString();
502        }
503
504        /** {@hide} */
505        public String getTelecomCallId() {
506            return mTelecomCallId;
507        }
508
509        /**
510         * @return The handle (e.g., phone number) to which the {@code Call} is currently
511         * connected.
512         */
513        public Uri getHandle() {
514            return mHandle;
515        }
516
517        /**
518         * @return The presentation requirements for the handle. See
519         * {@link TelecomManager} for valid values.
520         */
521        public int getHandlePresentation() {
522            return mHandlePresentation;
523        }
524
525        /**
526         * @return The display name for the caller.
527         */
528        public String getCallerDisplayName() {
529            return mCallerDisplayName;
530        }
531
532        /**
533         * @return The presentation requirements for the caller display name. See
534         * {@link TelecomManager} for valid values.
535         */
536        public int getCallerDisplayNamePresentation() {
537            return mCallerDisplayNamePresentation;
538        }
539
540        /**
541         * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being
542         * routed.
543         */
544        public PhoneAccountHandle getAccountHandle() {
545            return mAccountHandle;
546        }
547
548        /**
549         * @return A bitmask of the capabilities of the {@code Call}, as defined by the various
550         *         {@code CAPABILITY_*} constants in this class.
551         */
552        public int getCallCapabilities() {
553            return mCallCapabilities;
554        }
555
556        /**
557         * @return A bitmask of the properties of the {@code Call}, as defined by the various
558         *         {@code PROPERTY_*} constants in this class.
559         */
560        public int getCallProperties() {
561            return mCallProperties;
562        }
563
564        /**
565         * @return a bitmask of the audio routes available for the call.
566         *
567         * @hide
568         */
569        public int getSupportedAudioRoutes() {
570            return mSupportedAudioRoutes;
571        }
572
573        /**
574         * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
575         * by {@link android.telecom.DisconnectCause}.
576         */
577        public DisconnectCause getDisconnectCause() {
578            return mDisconnectCause;
579        }
580
581        /**
582         * Returns the time the {@link Call} connected (i.e. became active).  This information is
583         * updated periodically, but user interfaces should not rely on this to display the "call
584         * time clock".  For the time when the call was first added to Telecom, see
585         * {@link #getCreationTimeMillis()}.
586         *
587         * @return The time the {@link Call} connected in milliseconds since the epoch.
588         */
589        public final long getConnectTimeMillis() {
590            return mConnectTimeMillis;
591        }
592
593        /**
594         * @return Information about any calling gateway the {@code Call} may be using.
595         */
596        public GatewayInfo getGatewayInfo() {
597            return mGatewayInfo;
598        }
599
600        /**
601         * @return The video state of the {@code Call}.
602         */
603        public int getVideoState() {
604            return mVideoState;
605        }
606
607        /**
608         * @return The current {@link android.telecom.StatusHints}, or {@code null} if none
609         * have been set.
610         */
611        public StatusHints getStatusHints() {
612            return mStatusHints;
613        }
614
615        /**
616         * @return The extras associated with this call.
617         */
618        public Bundle getExtras() {
619            return mExtras;
620        }
621
622        /**
623         * @return The extras used with the original intent to place this call.
624         */
625        public Bundle getIntentExtras() {
626            return mIntentExtras;
627        }
628
629        /**
630         * Returns the time when the call was first created and added to Telecom.  This is the same
631         * time that is logged as the start time in the Call Log (see
632         * {@link android.provider.CallLog.Calls#DATE}).  To determine when the call was connected
633         * (became active), see {@link #getConnectTimeMillis()}.
634         *
635         * @return The creation time of the call, in millis since the epoch.
636         */
637        public long getCreationTimeMillis() {
638            return mCreationTimeMillis;
639        }
640
641        @Override
642        public boolean equals(Object o) {
643            if (o instanceof Details) {
644                Details d = (Details) o;
645                return
646                        Objects.equals(mHandle, d.mHandle) &&
647                        Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
648                        Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
649                        Objects.equals(mCallerDisplayNamePresentation,
650                                d.mCallerDisplayNamePresentation) &&
651                        Objects.equals(mAccountHandle, d.mAccountHandle) &&
652                        Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
653                        Objects.equals(mCallProperties, d.mCallProperties) &&
654                        Objects.equals(mDisconnectCause, d.mDisconnectCause) &&
655                        Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
656                        Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
657                        Objects.equals(mVideoState, d.mVideoState) &&
658                        Objects.equals(mStatusHints, d.mStatusHints) &&
659                        areBundlesEqual(mExtras, d.mExtras) &&
660                        areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
661                        Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis);
662            }
663            return false;
664        }
665
666        @Override
667        public int hashCode() {
668            return Objects.hash(mHandle,
669                            mHandlePresentation,
670                            mCallerDisplayName,
671                            mCallerDisplayNamePresentation,
672                            mAccountHandle,
673                            mCallCapabilities,
674                            mCallProperties,
675                            mDisconnectCause,
676                            mConnectTimeMillis,
677                            mGatewayInfo,
678                            mVideoState,
679                            mStatusHints,
680                            mExtras,
681                            mIntentExtras,
682                            mCreationTimeMillis);
683        }
684
685        /** {@hide} */
686        public Details(
687                String telecomCallId,
688                Uri handle,
689                int handlePresentation,
690                String callerDisplayName,
691                int callerDisplayNamePresentation,
692                PhoneAccountHandle accountHandle,
693                int capabilities,
694                int properties,
695                DisconnectCause disconnectCause,
696                long connectTimeMillis,
697                GatewayInfo gatewayInfo,
698                int videoState,
699                StatusHints statusHints,
700                Bundle extras,
701                Bundle intentExtras,
702                long creationTimeMillis) {
703            mTelecomCallId = telecomCallId;
704            mHandle = handle;
705            mHandlePresentation = handlePresentation;
706            mCallerDisplayName = callerDisplayName;
707            mCallerDisplayNamePresentation = callerDisplayNamePresentation;
708            mAccountHandle = accountHandle;
709            mCallCapabilities = capabilities;
710            mCallProperties = properties;
711            mDisconnectCause = disconnectCause;
712            mConnectTimeMillis = connectTimeMillis;
713            mGatewayInfo = gatewayInfo;
714            mVideoState = videoState;
715            mStatusHints = statusHints;
716            mExtras = extras;
717            mIntentExtras = intentExtras;
718            mCreationTimeMillis = creationTimeMillis;
719        }
720
721        /** {@hide} */
722        public static Details createFromParcelableCall(ParcelableCall parcelableCall) {
723            return new Details(
724                    parcelableCall.getId(),
725                    parcelableCall.getHandle(),
726                    parcelableCall.getHandlePresentation(),
727                    parcelableCall.getCallerDisplayName(),
728                    parcelableCall.getCallerDisplayNamePresentation(),
729                    parcelableCall.getAccountHandle(),
730                    parcelableCall.getCapabilities(),
731                    parcelableCall.getProperties(),
732                    parcelableCall.getDisconnectCause(),
733                    parcelableCall.getConnectTimeMillis(),
734                    parcelableCall.getGatewayInfo(),
735                    parcelableCall.getVideoState(),
736                    parcelableCall.getStatusHints(),
737                    parcelableCall.getExtras(),
738                    parcelableCall.getIntentExtras(),
739                    parcelableCall.getCreationTimeMillis());
740        }
741
742        @Override
743        public String toString() {
744            StringBuilder sb = new StringBuilder();
745            sb.append("[pa: ");
746            sb.append(mAccountHandle);
747            sb.append(", hdl: ");
748            sb.append(Log.pii(mHandle));
749            sb.append(", caps: ");
750            sb.append(capabilitiesToString(mCallCapabilities));
751            sb.append(", props: ");
752            sb.append(propertiesToString(mCallProperties));
753            sb.append("]");
754            return sb.toString();
755        }
756    }
757
758    /**
759     * Defines callbacks which inform the {@link InCallService} of changes to a {@link Call}.
760     * These callbacks can originate from the Telecom framework, or a {@link ConnectionService}
761     * implementation.
762     * <p>
763     * You can handle these callbacks by extending the {@link Callback} class and overriding the
764     * callbacks that your {@link InCallService} is interested in.  The callback methods include the
765     * {@link Call} for which the callback applies, allowing reuse of a single instance of your
766     * {@link Callback} implementation, if desired.
767     * <p>
768     * Use {@link Call#registerCallback(Callback)} to register your callback(s).  Ensure
769     * {@link Call#unregisterCallback(Callback)} is called when you no longer require callbacks
770     * (typically in {@link InCallService#onCallRemoved(Call)}).
771     * Note: Callbacks which occur before you call {@link Call#registerCallback(Callback)} will not
772     * reach your implementation of {@link Callback}, so it is important to register your callback
773     * as soon as your {@link InCallService} is notified of a new call via
774     * {@link InCallService#onCallAdded(Call)}.
775     */
776    public static abstract class Callback {
777        /**
778         * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
779         *
780         * @param call The {@code Call} invoking this method.
781         * @param state The new state of the {@code Call}.
782         */
783        public void onStateChanged(Call call, int state) {}
784
785        /**
786         * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
787         *
788         * @param call The {@code Call} invoking this method.
789         * @param parent The new parent of the {@code Call}.
790         */
791        public void onParentChanged(Call call, Call parent) {}
792
793        /**
794         * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
795         *
796         * @param call The {@code Call} invoking this method.
797         * @param children The new children of the {@code Call}.
798         */
799        public void onChildrenChanged(Call call, List<Call> children) {}
800
801        /**
802         * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
803         *
804         * @param call The {@code Call} invoking this method.
805         * @param details A {@code Details} object describing the {@code Call}.
806         */
807        public void onDetailsChanged(Call call, Details details) {}
808
809        /**
810         * Invoked when the text messages that can be used as responses to the incoming
811         * {@code Call} are loaded from the relevant database.
812         * See {@link #getCannedTextResponses()}.
813         *
814         * @param call The {@code Call} invoking this method.
815         * @param cannedTextResponses The text messages useable as responses.
816         */
817        public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
818
819        /**
820         * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
821         * character. This causes the post-dial signals to stop pending user confirmation. An
822         * implementation should present this choice to the user and invoke
823         * {@link #postDialContinue(boolean)} when the user makes the choice.
824         *
825         * @param call The {@code Call} invoking this method.
826         * @param remainingPostDialSequence The post-dial characters that remain to be sent.
827         */
828        public void onPostDialWait(Call call, String remainingPostDialSequence) {}
829
830        /**
831         * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed.
832         *
833         * @param call The {@code Call} invoking this method.
834         * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
835         */
836        public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
837
838        /**
839         * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
840         * up their UI for the {@code Call} in response to state transitions. Specifically,
841         * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
842         * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
843         * clients should wait for this method to be invoked.
844         *
845         * @param call The {@code Call} being destroyed.
846         */
847        public void onCallDestroyed(Call call) {}
848
849        /**
850         * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be
851         * conferenced.
852         *
853         * @param call The {@code Call} being updated.
854         * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be
855         *          conferenced.
856         */
857        public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
858
859        /**
860         * Invoked when a {@link Call} receives an event from its associated {@link Connection}.
861         * <p>
862         * Where possible, the Call should make an attempt to handle {@link Connection} events which
863         * are part of the {@code android.telecom.*} namespace.  The Call should ignore any events
864         * it does not wish to handle.  Unexpected events should be handled gracefully, as it is
865         * possible that a {@link ConnectionService} has defined its own Connection events which a
866         * Call is not aware of.
867         * <p>
868         * See {@link Connection#sendConnectionEvent(String, Bundle)}.
869         *
870         * @param call The {@code Call} receiving the event.
871         * @param event The event.
872         * @param extras Extras associated with the connection event.
873         */
874        public void onConnectionEvent(Call call, String event, Bundle extras) {}
875
876        /**
877         * Invoked when the RTT mode changes for this call.
878         * @param call The call whose RTT mode has changed.
879         * @param mode the new RTT mode, one of
880         * {@link RttCall#RTT_MODE_FULL}, {@link RttCall#RTT_MODE_HCO},
881         *             or {@link RttCall#RTT_MODE_VCO}
882         */
883        public void onRttModeChanged(Call call, int mode) {}
884
885        /**
886         * Invoked when the call's RTT status changes, either from off to on or from on to off.
887         * @param call The call whose RTT status has changed.
888         * @param enabled whether RTT is now enabled or disabled
889         * @param rttCall the {@link RttCall} object to use for reading and writing if RTT is now
890         *                on, null otherwise.
891         */
892        public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {}
893
894        /**
895         * Invoked when the remote end of the connection has requested that an RTT communication
896         * channel be opened. A response to this should be sent via {@link #respondToRttRequest}
897         * with the same ID that this method is invoked with.
898         * @param call The call which the RTT request was placed on
899         * @param id The ID of the request.
900         */
901        public void onRttRequest(Call call, int id) {}
902
903        /**
904         * Invoked when the RTT session failed to initiate for some reason, including rejection
905         * by the remote party.
906         * @param call The call which the RTT initiation failure occurred on.
907         * @param reason One of the status codes defined in
908         *               {@link android.telecom.Connection.RttModifyStatus}, with the exception of
909         *               {@link android.telecom.Connection.RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
910         */
911        public void onRttInitiationFailure(Call call, int reason) {}
912    }
913
914    /**
915     * A class that holds the state that describes the state of the RTT channel to the remote
916     * party, if it is active.
917     */
918    public static final class RttCall {
919        /** @hide */
920        @Retention(RetentionPolicy.SOURCE)
921        @IntDef({RTT_MODE_INVALID, RTT_MODE_FULL, RTT_MODE_HCO, RTT_MODE_VCO})
922        public @interface RttAudioMode {}
923
924        /**
925         * For metrics use. Default value in the proto.
926         * @hide
927         */
928        public static final int RTT_MODE_INVALID = 0;
929
930        /**
931         * Indicates that there should be a bidirectional audio stream between the two parties
932         * on the call.
933         */
934        public static final int RTT_MODE_FULL = 1;
935
936        /**
937         * Indicates that the local user should be able to hear the audio stream from the remote
938         * user, but not vice versa. Equivalent to muting the microphone.
939         */
940        public static final int RTT_MODE_HCO = 2;
941
942        /**
943         * Indicates that the remote user should be able to hear the audio stream from the local
944         * user, but not vice versa. Equivalent to setting the volume to zero.
945         */
946        public static final int RTT_MODE_VCO = 3;
947
948        private static final int READ_BUFFER_SIZE = 1000;
949
950        private InputStreamReader mReceiveStream;
951        private OutputStreamWriter mTransmitStream;
952        private int mRttMode;
953        private final InCallAdapter mInCallAdapter;
954        private final String mTelecomCallId;
955        private char[] mReadBuffer = new char[READ_BUFFER_SIZE];
956
957        /**
958         * @hide
959         */
960        public RttCall(String telecomCallId, InputStreamReader receiveStream,
961                OutputStreamWriter transmitStream, int mode, InCallAdapter inCallAdapter) {
962            mTelecomCallId = telecomCallId;
963            mReceiveStream = receiveStream;
964            mTransmitStream = transmitStream;
965            mRttMode = mode;
966            mInCallAdapter = inCallAdapter;
967        }
968
969        /**
970         * Returns the current RTT audio mode.
971         * @return Current RTT audio mode. One of {@link #RTT_MODE_FULL}, {@link #RTT_MODE_VCO}, or
972         * {@link #RTT_MODE_HCO}.
973         */
974        public int getRttAudioMode() {
975            return mRttMode;
976        }
977
978        /**
979         * Sets the RTT audio mode. The requested mode change will be communicated through
980         * {@link Callback#onRttModeChanged(Call, int)}.
981         * @param mode The desired RTT audio mode, one of {@link #RTT_MODE_FULL},
982         * {@link #RTT_MODE_VCO}, or {@link #RTT_MODE_HCO}.
983         */
984        public void setRttMode(@RttAudioMode int mode) {
985            mInCallAdapter.setRttMode(mTelecomCallId, mode);
986        }
987
988        /**
989         * Writes the string {@param input} into the outgoing text stream for this RTT call. Since
990         * RTT transmits text in real-time, this method should be called once for each character
991         * the user enters into the device.
992         *
993         * This method is not thread-safe -- calling it from multiple threads simultaneously may
994         * lead to interleaved text.
995         * @param input The message to send to the remote user.
996         */
997        public void write(String input) throws IOException {
998            mTransmitStream.write(input);
999            mTransmitStream.flush();
1000        }
1001
1002        /**
1003         * Reads a string from the remote user, blocking if there is no data available. Returns
1004         * {@code null} if the RTT conversation has been terminated and there is no further data
1005         * to read.
1006         *
1007         * This method is not thread-safe -- calling it from multiple threads simultaneously may
1008         * lead to interleaved text.
1009         * @return A string containing text sent by the remote user, or {@code null} if the
1010         * conversation has been terminated or if there was an error while reading.
1011         */
1012        public String read() {
1013            try {
1014                int numRead = mReceiveStream.read(mReadBuffer, 0, READ_BUFFER_SIZE);
1015                if (numRead < 0) {
1016                    return null;
1017                }
1018                return new String(mReadBuffer, 0, numRead);
1019            } catch (IOException e) {
1020                Log.w(this, "Exception encountered when reading from InputStreamReader: %s", e);
1021                return null;
1022            }
1023        }
1024    }
1025
1026    /**
1027     * @deprecated Use {@code Call.Callback} instead.
1028     * @hide
1029     */
1030    @Deprecated
1031    @SystemApi
1032    public static abstract class Listener extends Callback { }
1033
1034    private final Phone mPhone;
1035    private final String mTelecomCallId;
1036    private final InCallAdapter mInCallAdapter;
1037    private final List<String> mChildrenIds = new ArrayList<>();
1038    private final List<Call> mChildren = new ArrayList<>();
1039    private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
1040    private final List<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArrayList<>();
1041    private final List<Call> mConferenceableCalls = new ArrayList<>();
1042    private final List<Call> mUnmodifiableConferenceableCalls =
1043            Collections.unmodifiableList(mConferenceableCalls);
1044
1045    private boolean mChildrenCached;
1046    private String mParentId = null;
1047    private int mState;
1048    private List<String> mCannedTextResponses = null;
1049    private String mCallingPackage;
1050    private int mTargetSdkVersion;
1051    private String mRemainingPostDialSequence;
1052    private VideoCallImpl mVideoCallImpl;
1053    private RttCall mRttCall;
1054    private Details mDetails;
1055    private Bundle mExtras;
1056
1057    /**
1058     * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
1059     *
1060     * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
1061     * remaining or this {@code Call} is not in a post-dial state.
1062     */
1063    public String getRemainingPostDialSequence() {
1064        return mRemainingPostDialSequence;
1065    }
1066
1067    /**
1068     * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
1069     * @param videoState The video state in which to answer the call.
1070     */
1071    public void answer(int videoState) {
1072        mInCallAdapter.answerCall(mTelecomCallId, videoState);
1073    }
1074
1075    /**
1076     * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
1077     *
1078     * @param rejectWithMessage Whether to reject with a text message.
1079     * @param textMessage An optional text message with which to respond.
1080     */
1081    public void reject(boolean rejectWithMessage, String textMessage) {
1082        mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage);
1083    }
1084
1085    /**
1086     * Instructs this {@code Call} to disconnect.
1087     */
1088    public void disconnect() {
1089        mInCallAdapter.disconnectCall(mTelecomCallId);
1090    }
1091
1092    /**
1093     * Instructs this {@code Call} to go on hold.
1094     */
1095    public void hold() {
1096        mInCallAdapter.holdCall(mTelecomCallId);
1097    }
1098
1099    /**
1100     * Instructs this {@link #STATE_HOLDING} call to release from hold.
1101     */
1102    public void unhold() {
1103        mInCallAdapter.unholdCall(mTelecomCallId);
1104    }
1105
1106    /**
1107     * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
1108     *
1109     * Any other currently playing DTMF tone in the specified call is immediately stopped.
1110     *
1111     * @param digit A character representing the DTMF digit for which to play the tone. This
1112     *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
1113     */
1114    public void playDtmfTone(char digit) {
1115        mInCallAdapter.playDtmfTone(mTelecomCallId, digit);
1116    }
1117
1118    /**
1119     * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
1120     * currently playing.
1121     *
1122     * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
1123     * currently playing, this method will do nothing.
1124     */
1125    public void stopDtmfTone() {
1126        mInCallAdapter.stopDtmfTone(mTelecomCallId);
1127    }
1128
1129    /**
1130     * Instructs this {@code Call} to continue playing a post-dial DTMF string.
1131     *
1132     * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
1133     * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
1134     *
1135     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
1136     * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
1137     *
1138     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
1139     * {@code Call} will pause playing the tones and notify callbacks via
1140     * {@link Callback#onPostDialWait(Call, String)}. At this point, the in-call app
1141     * should display to the user an indication of this state and an affordance to continue
1142     * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
1143     * app should invoke the {@link #postDialContinue(boolean)} method.
1144     *
1145     * @param proceed Whether or not to continue with the post-dial sequence.
1146     */
1147    public void postDialContinue(boolean proceed) {
1148        mInCallAdapter.postDialContinue(mTelecomCallId, proceed);
1149    }
1150
1151    /**
1152     * Notifies this {@code Call} that an account has been selected and to proceed with placing
1153     * an outgoing call. Optionally sets this account as the default account.
1154     */
1155    public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1156        mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle, setDefault);
1157
1158    }
1159
1160    /**
1161     * Instructs this {@code Call} to enter a conference.
1162     *
1163     * @param callToConferenceWith The other call with which to conference.
1164     */
1165    public void conference(Call callToConferenceWith) {
1166        if (callToConferenceWith != null) {
1167            mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId);
1168        }
1169    }
1170
1171    /**
1172     * Instructs this {@code Call} to split from any conference call with which it may be
1173     * connected.
1174     */
1175    public void splitFromConference() {
1176        mInCallAdapter.splitFromConference(mTelecomCallId);
1177    }
1178
1179    /**
1180     * Merges the calls within this conference. See {@link Details#CAPABILITY_MERGE_CONFERENCE}.
1181     */
1182    public void mergeConference() {
1183        mInCallAdapter.mergeConference(mTelecomCallId);
1184    }
1185
1186    /**
1187     * Swaps the calls within this conference. See {@link Details#CAPABILITY_SWAP_CONFERENCE}.
1188     */
1189    public void swapConference() {
1190        mInCallAdapter.swapConference(mTelecomCallId);
1191    }
1192
1193    /**
1194     * Initiates a request to the {@link ConnectionService} to pull an external call to the local
1195     * device.
1196     * <p>
1197     * Calls to this method are ignored if the call does not have the
1198     * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} property set.
1199     * <p>
1200     * An {@link InCallService} will only see calls which support this method if it has the
1201     * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
1202     * in its manifest.
1203     */
1204    public void pullExternalCall() {
1205        // If this isn't an external call, ignore the request.
1206        if (!mDetails.hasProperty(Details.PROPERTY_IS_EXTERNAL_CALL)) {
1207            return;
1208        }
1209
1210        mInCallAdapter.pullExternalCall(mTelecomCallId);
1211    }
1212
1213    /**
1214     * Sends a {@code Call} event from this {@code Call} to the associated {@link Connection} in
1215     * the {@link ConnectionService}.
1216     * <p>
1217     * Call events are used to communicate point in time information from an {@link InCallService}
1218     * to a {@link ConnectionService}.  A {@link ConnectionService} implementation could define
1219     * events which enable the {@link InCallService}, for example, toggle a unique feature of the
1220     * {@link ConnectionService}.
1221     * <p>
1222     * A {@link ConnectionService} can communicate to the {@link InCallService} using
1223     * {@link Connection#sendConnectionEvent(String, Bundle)}.
1224     * <p>
1225     * Events are exposed to {@link ConnectionService} implementations via
1226     * {@link android.telecom.Connection#onCallEvent(String, Bundle)}.
1227     * <p>
1228     * No assumptions should be made as to how a {@link ConnectionService} will handle these events.
1229     * The {@link InCallService} must assume that the {@link ConnectionService} could chose to
1230     * ignore some events altogether.
1231     * <p>
1232     * Events should be fully qualified (e.g., {@code com.example.event.MY_EVENT}) to avoid
1233     * conflicts between {@link InCallService} implementations.  Further, {@link InCallService}
1234     * implementations shall not re-purpose events in the {@code android.*} namespace, nor shall
1235     * they define their own event types in this namespace.  When defining a custom event type,
1236     * ensure the contents of the extras {@link Bundle} is clearly defined.  Extra keys for this
1237     * bundle should be named similar to the event type (e.g. {@code com.example.extra.MY_EXTRA}).
1238     * <p>
1239     * When defining events and the associated extras, it is important to keep their behavior
1240     * consistent when the associated {@link InCallService} is updated.  Support for deprecated
1241     * events/extras should me maintained to ensure backwards compatibility with older
1242     * {@link ConnectionService} implementations which were built to support the older behavior.
1243     *
1244     * @param event The connection event.
1245     * @param extras Bundle containing extra information associated with the event.
1246     */
1247    public void sendCallEvent(String event, Bundle extras) {
1248        mInCallAdapter.sendCallEvent(mTelecomCallId, event, extras);
1249    }
1250
1251    /**
1252     * Sends an RTT upgrade request to the remote end of the connection. Success is not
1253     * guaranteed, and notification of success will be via the
1254     * {@link Callback#onRttStatusChanged(Call, boolean, RttCall)} callback.
1255     */
1256    public void sendRttRequest() {
1257        mInCallAdapter.sendRttRequest(mTelecomCallId);
1258    }
1259
1260    /**
1261     * Responds to an RTT request received via the {@link Callback#onRttRequest(Call, int)} )}
1262     * callback.
1263     * The ID used here should be the same as the ID that was received via the callback.
1264     * @param id The request ID received via {@link Callback#onRttRequest(Call, int)}
1265     * @param accept {@code true} if the RTT request should be accepted, {@code false} otherwise.
1266     */
1267    public void respondToRttRequest(int id, boolean accept) {
1268        mInCallAdapter.respondToRttRequest(mTelecomCallId, id, accept);
1269    }
1270
1271    /**
1272     * Terminate the RTT session on this call. The resulting state change will be notified via
1273     * the {@link Callback#onRttStatusChanged(Call, boolean, RttCall)} callback.
1274     */
1275    public void stopRtt() {
1276        mInCallAdapter.stopRtt(mTelecomCallId);
1277    }
1278
1279    /**
1280     * Adds some extras to this {@link Call}.  Existing keys are replaced and new ones are
1281     * added.
1282     * <p>
1283     * No assumptions should be made as to how an In-Call UI or service will handle these
1284     * extras.  Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
1285     *
1286     * @param extras The extras to add.
1287     */
1288    public final void putExtras(Bundle extras) {
1289        if (extras == null) {
1290            return;
1291        }
1292
1293        if (mExtras == null) {
1294            mExtras = new Bundle();
1295        }
1296        mExtras.putAll(extras);
1297        mInCallAdapter.putExtras(mTelecomCallId, extras);
1298    }
1299
1300    /**
1301     * Adds a boolean extra to this {@link Call}.
1302     *
1303     * @param key The extra key.
1304     * @param value The value.
1305     * @hide
1306     */
1307    public final void putExtra(String key, boolean value) {
1308        if (mExtras == null) {
1309            mExtras = new Bundle();
1310        }
1311        mExtras.putBoolean(key, value);
1312        mInCallAdapter.putExtra(mTelecomCallId, key, value);
1313    }
1314
1315    /**
1316     * Adds an integer extra to this {@link Call}.
1317     *
1318     * @param key The extra key.
1319     * @param value The value.
1320     * @hide
1321     */
1322    public final void putExtra(String key, int value) {
1323        if (mExtras == null) {
1324            mExtras = new Bundle();
1325        }
1326        mExtras.putInt(key, value);
1327        mInCallAdapter.putExtra(mTelecomCallId, key, value);
1328    }
1329
1330    /**
1331     * Adds a string extra to this {@link Call}.
1332     *
1333     * @param key The extra key.
1334     * @param value The value.
1335     * @hide
1336     */
1337    public final void putExtra(String key, String value) {
1338        if (mExtras == null) {
1339            mExtras = new Bundle();
1340        }
1341        mExtras.putString(key, value);
1342        mInCallAdapter.putExtra(mTelecomCallId, key, value);
1343    }
1344
1345    /**
1346     * Removes extras from this {@link Call}.
1347     *
1348     * @param keys The keys of the extras to remove.
1349     */
1350    public final void removeExtras(List<String> keys) {
1351        if (mExtras != null) {
1352            for (String key : keys) {
1353                mExtras.remove(key);
1354            }
1355            if (mExtras.size() == 0) {
1356                mExtras = null;
1357            }
1358        }
1359        mInCallAdapter.removeExtras(mTelecomCallId, keys);
1360    }
1361
1362    /**
1363     * Removes extras from this {@link Call}.
1364     *
1365     * @param keys The keys of the extras to remove.
1366     */
1367    public final void removeExtras(String ... keys) {
1368        removeExtras(Arrays.asList(keys));
1369    }
1370
1371    /**
1372     * Obtains the parent of this {@code Call} in a conference, if any.
1373     *
1374     * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
1375     * child of any conference {@code Call}s.
1376     */
1377    public Call getParent() {
1378        if (mParentId != null) {
1379            return mPhone.internalGetCallByTelecomId(mParentId);
1380        }
1381        return null;
1382    }
1383
1384    /**
1385     * Obtains the children of this conference {@code Call}, if any.
1386     *
1387     * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
1388     * {@code List} otherwise.
1389     */
1390    public List<Call> getChildren() {
1391        if (!mChildrenCached) {
1392            mChildrenCached = true;
1393            mChildren.clear();
1394
1395            for(String id : mChildrenIds) {
1396                Call call = mPhone.internalGetCallByTelecomId(id);
1397                if (call == null) {
1398                    // At least one child was still not found, so do not save true for "cached"
1399                    mChildrenCached = false;
1400                } else {
1401                    mChildren.add(call);
1402                }
1403            }
1404        }
1405
1406        return mUnmodifiableChildren;
1407    }
1408
1409    /**
1410     * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference.
1411     *
1412     * @return The list of conferenceable {@code Call}s.
1413     */
1414    public List<Call> getConferenceableCalls() {
1415        return mUnmodifiableConferenceableCalls;
1416    }
1417
1418    /**
1419     * Obtains the state of this {@code Call}.
1420     *
1421     * @return A state value, chosen from the {@code STATE_*} constants.
1422     */
1423    public int getState() {
1424        return mState;
1425    }
1426
1427    /**
1428     * Obtains a list of canned, pre-configured message responses to present to the user as
1429     * ways of rejecting this {@code Call} using via a text message.
1430     *
1431     * @see #reject(boolean, String)
1432     *
1433     * @return A list of canned text message responses.
1434     */
1435    public List<String> getCannedTextResponses() {
1436        return mCannedTextResponses;
1437    }
1438
1439    /**
1440     * Obtains an object that can be used to display video from this {@code Call}.
1441     *
1442     * @return An {@code Call.VideoCall}.
1443     */
1444    public InCallService.VideoCall getVideoCall() {
1445        return mVideoCallImpl;
1446    }
1447
1448    /**
1449     * Obtains an object containing call details.
1450     *
1451     * @return A {@link Details} object. Depending on the state of the {@code Call}, the
1452     * result may be {@code null}.
1453     */
1454    public Details getDetails() {
1455        return mDetails;
1456    }
1457
1458    /**
1459     * Returns this call's RttCall object. The {@link RttCall} instance is used to send and
1460     * receive RTT text data, as well as to change the RTT mode.
1461     * @return A {@link Call.RttCall}. {@code null} if there is no active RTT connection.
1462     */
1463    public @Nullable RttCall getRttCall() {
1464        return mRttCall;
1465    }
1466
1467    /**
1468     * Returns whether this call has an active RTT connection.
1469     * @return true if there is a connection, false otherwise.
1470     */
1471    public boolean isRttActive() {
1472        return mRttCall != null;
1473    }
1474
1475    /**
1476     * Registers a callback to this {@code Call}.
1477     *
1478     * @param callback A {@code Callback}.
1479     */
1480    public void registerCallback(Callback callback) {
1481        registerCallback(callback, new Handler());
1482    }
1483
1484    /**
1485     * Registers a callback to this {@code Call}.
1486     *
1487     * @param callback A {@code Callback}.
1488     * @param handler A handler which command and status changes will be delivered to.
1489     */
1490    public void registerCallback(Callback callback, Handler handler) {
1491        unregisterCallback(callback);
1492        // Don't allow new callback registration if the call is already being destroyed.
1493        if (callback != null && handler != null && mState != STATE_DISCONNECTED) {
1494            mCallbackRecords.add(new CallbackRecord<Callback>(callback, handler));
1495        }
1496    }
1497
1498    /**
1499     * Unregisters a callback from this {@code Call}.
1500     *
1501     * @param callback A {@code Callback}.
1502     */
1503    public void unregisterCallback(Callback callback) {
1504        // Don't allow callback deregistration if the call is already being destroyed.
1505        if (callback != null && mState != STATE_DISCONNECTED) {
1506            for (CallbackRecord<Callback> record : mCallbackRecords) {
1507                if (record.getCallback() == callback) {
1508                    mCallbackRecords.remove(record);
1509                    break;
1510                }
1511            }
1512        }
1513    }
1514
1515    @Override
1516    public String toString() {
1517        return new StringBuilder().
1518                append("Call [id: ").
1519                append(mTelecomCallId).
1520                append(", state: ").
1521                append(stateToString(mState)).
1522                append(", details: ").
1523                append(mDetails).
1524                append("]").toString();
1525    }
1526
1527    /**
1528     * @param state An integer value of a {@code STATE_*} constant.
1529     * @return A string representation of the value.
1530     */
1531    private static String stateToString(int state) {
1532        switch (state) {
1533            case STATE_NEW:
1534                return "NEW";
1535            case STATE_RINGING:
1536                return "RINGING";
1537            case STATE_DIALING:
1538                return "DIALING";
1539            case STATE_ACTIVE:
1540                return "ACTIVE";
1541            case STATE_HOLDING:
1542                return "HOLDING";
1543            case STATE_DISCONNECTED:
1544                return "DISCONNECTED";
1545            case STATE_CONNECTING:
1546                return "CONNECTING";
1547            case STATE_DISCONNECTING:
1548                return "DISCONNECTING";
1549            case STATE_SELECT_PHONE_ACCOUNT:
1550                return "SELECT_PHONE_ACCOUNT";
1551            default:
1552                Log.w(Call.class, "Unknown state %d", state);
1553                return "UNKNOWN";
1554        }
1555    }
1556
1557    /**
1558     * Adds a listener to this {@code Call}.
1559     *
1560     * @param listener A {@code Listener}.
1561     * @deprecated Use {@link #registerCallback} instead.
1562     * @hide
1563     */
1564    @Deprecated
1565    @SystemApi
1566    public void addListener(Listener listener) {
1567        registerCallback(listener);
1568    }
1569
1570    /**
1571     * Removes a listener from this {@code Call}.
1572     *
1573     * @param listener A {@code Listener}.
1574     * @deprecated Use {@link #unregisterCallback} instead.
1575     * @hide
1576     */
1577    @Deprecated
1578    @SystemApi
1579    public void removeListener(Listener listener) {
1580        unregisterCallback(listener);
1581    }
1582
1583    /** {@hide} */
1584    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, String callingPackage,
1585         int targetSdkVersion) {
1586        mPhone = phone;
1587        mTelecomCallId = telecomCallId;
1588        mInCallAdapter = inCallAdapter;
1589        mState = STATE_NEW;
1590        mCallingPackage = callingPackage;
1591        mTargetSdkVersion = targetSdkVersion;
1592    }
1593
1594    /** {@hide} */
1595    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state,
1596            String callingPackage, int targetSdkVersion) {
1597        mPhone = phone;
1598        mTelecomCallId = telecomCallId;
1599        mInCallAdapter = inCallAdapter;
1600        mState = state;
1601        mCallingPackage = callingPackage;
1602        mTargetSdkVersion = targetSdkVersion;
1603    }
1604
1605    /** {@hide} */
1606    final String internalGetCallId() {
1607        return mTelecomCallId;
1608    }
1609
1610    /** {@hide} */
1611    final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
1612
1613        // First, we update the internal state as far as possible before firing any updates.
1614        Details details = Details.createFromParcelableCall(parcelableCall);
1615        boolean detailsChanged = !Objects.equals(mDetails, details);
1616        if (detailsChanged) {
1617            mDetails = details;
1618        }
1619
1620        boolean cannedTextResponsesChanged = false;
1621        if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
1622                && !parcelableCall.getCannedSmsResponses().isEmpty()) {
1623            mCannedTextResponses =
1624                    Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
1625            cannedTextResponsesChanged = true;
1626        }
1627
1628        VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(mCallingPackage,
1629                mTargetSdkVersion);
1630        boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
1631                !Objects.equals(mVideoCallImpl, newVideoCallImpl);
1632        if (videoCallChanged) {
1633            mVideoCallImpl = newVideoCallImpl;
1634        }
1635        if (mVideoCallImpl != null) {
1636            mVideoCallImpl.setVideoState(getDetails().getVideoState());
1637        }
1638
1639        int state = parcelableCall.getState();
1640        boolean stateChanged = mState != state;
1641        if (stateChanged) {
1642            mState = state;
1643        }
1644
1645        String parentId = parcelableCall.getParentCallId();
1646        boolean parentChanged = !Objects.equals(mParentId, parentId);
1647        if (parentChanged) {
1648            mParentId = parentId;
1649        }
1650
1651        List<String> childCallIds = parcelableCall.getChildCallIds();
1652        boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds);
1653        if (childrenChanged) {
1654            mChildrenIds.clear();
1655            mChildrenIds.addAll(parcelableCall.getChildCallIds());
1656            mChildrenCached = false;
1657        }
1658
1659        List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds();
1660        List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size());
1661        for (String otherId : conferenceableCallIds) {
1662            if (callIdMap.containsKey(otherId)) {
1663                conferenceableCalls.add(callIdMap.get(otherId));
1664            }
1665        }
1666
1667        if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) {
1668            mConferenceableCalls.clear();
1669            mConferenceableCalls.addAll(conferenceableCalls);
1670            fireConferenceableCallsChanged();
1671        }
1672
1673        boolean isRttChanged = false;
1674        boolean rttModeChanged = false;
1675        if (parcelableCall.getParcelableRttCall() != null && parcelableCall.getIsRttCallChanged()) {
1676            ParcelableRttCall parcelableRttCall = parcelableCall.getParcelableRttCall();
1677            InputStreamReader receiveStream = new InputStreamReader(
1678                    new ParcelFileDescriptor.AutoCloseInputStream(
1679                            parcelableRttCall.getReceiveStream()),
1680                    StandardCharsets.UTF_8);
1681            OutputStreamWriter transmitStream = new OutputStreamWriter(
1682                    new ParcelFileDescriptor.AutoCloseOutputStream(
1683                            parcelableRttCall.getTransmitStream()),
1684                    StandardCharsets.UTF_8);
1685            RttCall newRttCall = new Call.RttCall(mTelecomCallId,
1686                    receiveStream, transmitStream, parcelableRttCall.getRttMode(), mInCallAdapter);
1687            if (mRttCall == null) {
1688                isRttChanged = true;
1689            } else if (mRttCall.getRttAudioMode() != newRttCall.getRttAudioMode()) {
1690                rttModeChanged = true;
1691            }
1692            mRttCall = newRttCall;
1693        } else if (mRttCall != null && parcelableCall.getParcelableRttCall() == null
1694                && parcelableCall.getIsRttCallChanged()) {
1695            isRttChanged = true;
1696            mRttCall = null;
1697        }
1698
1699        // Now we fire updates, ensuring that any client who listens to any of these notifications
1700        // gets the most up-to-date state.
1701
1702        if (stateChanged) {
1703            fireStateChanged(mState);
1704        }
1705        if (detailsChanged) {
1706            fireDetailsChanged(mDetails);
1707        }
1708        if (cannedTextResponsesChanged) {
1709            fireCannedTextResponsesLoaded(mCannedTextResponses);
1710        }
1711        if (videoCallChanged) {
1712            fireVideoCallChanged(mVideoCallImpl);
1713        }
1714        if (parentChanged) {
1715            fireParentChanged(getParent());
1716        }
1717        if (childrenChanged) {
1718            fireChildrenChanged(getChildren());
1719        }
1720        if (isRttChanged) {
1721            fireOnIsRttChanged(mRttCall != null, mRttCall);
1722        }
1723        if (rttModeChanged) {
1724            fireOnRttModeChanged(mRttCall.getRttAudioMode());
1725        }
1726
1727        // If we have transitioned to DISCONNECTED, that means we need to notify clients and
1728        // remove ourselves from the Phone. Note that we do this after completing all state updates
1729        // so a client can cleanly transition all their UI to the state appropriate for a
1730        // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
1731        if (mState == STATE_DISCONNECTED) {
1732            fireCallDestroyed();
1733        }
1734    }
1735
1736    /** {@hide} */
1737    final void internalSetPostDialWait(String remaining) {
1738        mRemainingPostDialSequence = remaining;
1739        firePostDialWait(mRemainingPostDialSequence);
1740    }
1741
1742    /** {@hide} */
1743    final void internalSetDisconnected() {
1744        if (mState != Call.STATE_DISCONNECTED) {
1745            mState = Call.STATE_DISCONNECTED;
1746            fireStateChanged(mState);
1747            fireCallDestroyed();
1748        }
1749    }
1750
1751    /** {@hide} */
1752    final void internalOnConnectionEvent(String event, Bundle extras) {
1753        fireOnConnectionEvent(event, extras);
1754    }
1755
1756    /** {@hide} */
1757    final void internalOnRttUpgradeRequest(final int requestId) {
1758        for (CallbackRecord<Callback> record : mCallbackRecords) {
1759            final Call call = this;
1760            final Callback callback = record.getCallback();
1761            record.getHandler().post(() -> callback.onRttRequest(call, requestId));
1762        }
1763    }
1764
1765    /** @hide */
1766    final void internalOnRttInitiationFailure(int reason) {
1767        for (CallbackRecord<Callback> record : mCallbackRecords) {
1768            final Call call = this;
1769            final Callback callback = record.getCallback();
1770            record.getHandler().post(() -> callback.onRttInitiationFailure(call, reason));
1771        }
1772    }
1773
1774    private void fireStateChanged(final int newState) {
1775        for (CallbackRecord<Callback> record : mCallbackRecords) {
1776            final Call call = this;
1777            final Callback callback = record.getCallback();
1778            record.getHandler().post(new Runnable() {
1779                @Override
1780                public void run() {
1781                    callback.onStateChanged(call, newState);
1782                }
1783            });
1784        }
1785    }
1786
1787    private void fireParentChanged(final Call newParent) {
1788        for (CallbackRecord<Callback> record : mCallbackRecords) {
1789            final Call call = this;
1790            final Callback callback = record.getCallback();
1791            record.getHandler().post(new Runnable() {
1792                @Override
1793                public void run() {
1794                    callback.onParentChanged(call, newParent);
1795                }
1796            });
1797        }
1798    }
1799
1800    private void fireChildrenChanged(final List<Call> children) {
1801        for (CallbackRecord<Callback> record : mCallbackRecords) {
1802            final Call call = this;
1803            final Callback callback = record.getCallback();
1804            record.getHandler().post(new Runnable() {
1805                @Override
1806                public void run() {
1807                    callback.onChildrenChanged(call, children);
1808                }
1809            });
1810        }
1811    }
1812
1813    private void fireDetailsChanged(final Details details) {
1814        for (CallbackRecord<Callback> record : mCallbackRecords) {
1815            final Call call = this;
1816            final Callback callback = record.getCallback();
1817            record.getHandler().post(new Runnable() {
1818                @Override
1819                public void run() {
1820                    callback.onDetailsChanged(call, details);
1821                }
1822            });
1823        }
1824    }
1825
1826    private void fireCannedTextResponsesLoaded(final List<String> cannedTextResponses) {
1827        for (CallbackRecord<Callback> record : mCallbackRecords) {
1828            final Call call = this;
1829            final Callback callback = record.getCallback();
1830            record.getHandler().post(new Runnable() {
1831                @Override
1832                public void run() {
1833                    callback.onCannedTextResponsesLoaded(call, cannedTextResponses);
1834                }
1835            });
1836        }
1837    }
1838
1839    private void fireVideoCallChanged(final InCallService.VideoCall videoCall) {
1840        for (CallbackRecord<Callback> record : mCallbackRecords) {
1841            final Call call = this;
1842            final Callback callback = record.getCallback();
1843            record.getHandler().post(new Runnable() {
1844                @Override
1845                public void run() {
1846                    callback.onVideoCallChanged(call, videoCall);
1847                }
1848            });
1849        }
1850    }
1851
1852    private void firePostDialWait(final String remainingPostDialSequence) {
1853        for (CallbackRecord<Callback> record : mCallbackRecords) {
1854            final Call call = this;
1855            final Callback callback = record.getCallback();
1856            record.getHandler().post(new Runnable() {
1857                @Override
1858                public void run() {
1859                    callback.onPostDialWait(call, remainingPostDialSequence);
1860                }
1861            });
1862        }
1863    }
1864
1865    private void fireCallDestroyed() {
1866        /**
1867         * To preserve the ordering of the Call's onCallDestroyed callback and Phone's
1868         * onCallRemoved callback, we remove this call from the Phone's record
1869         * only once all of the registered onCallDestroyed callbacks are executed.
1870         * All the callbacks get removed from our records as a part of this operation
1871         * since onCallDestroyed is the final callback.
1872         */
1873        final Call call = this;
1874        if (mCallbackRecords.isEmpty()) {
1875            // No callbacks registered, remove the call from Phone's record.
1876            mPhone.internalRemoveCall(call);
1877        }
1878        for (final CallbackRecord<Callback> record : mCallbackRecords) {
1879            final Callback callback = record.getCallback();
1880            record.getHandler().post(new Runnable() {
1881                @Override
1882                public void run() {
1883                    boolean isFinalRemoval = false;
1884                    RuntimeException toThrow = null;
1885                    try {
1886                        callback.onCallDestroyed(call);
1887                    } catch (RuntimeException e) {
1888                            toThrow = e;
1889                    }
1890                    synchronized(Call.this) {
1891                        mCallbackRecords.remove(record);
1892                        if (mCallbackRecords.isEmpty()) {
1893                            isFinalRemoval = true;
1894                        }
1895                    }
1896                    if (isFinalRemoval) {
1897                        mPhone.internalRemoveCall(call);
1898                    }
1899                    if (toThrow != null) {
1900                        throw toThrow;
1901                    }
1902                }
1903            });
1904        }
1905    }
1906
1907    private void fireConferenceableCallsChanged() {
1908        for (CallbackRecord<Callback> record : mCallbackRecords) {
1909            final Call call = this;
1910            final Callback callback = record.getCallback();
1911            record.getHandler().post(new Runnable() {
1912                @Override
1913                public void run() {
1914                    callback.onConferenceableCallsChanged(call, mUnmodifiableConferenceableCalls);
1915                }
1916            });
1917        }
1918    }
1919
1920    /**
1921     * Notifies listeners of an incoming connection event.
1922     * <p>
1923     * Connection events are issued via {@link Connection#sendConnectionEvent(String, Bundle)}.
1924     *
1925     * @param event
1926     * @param extras
1927     */
1928    private void fireOnConnectionEvent(final String event, final Bundle extras) {
1929        for (CallbackRecord<Callback> record : mCallbackRecords) {
1930            final Call call = this;
1931            final Callback callback = record.getCallback();
1932            record.getHandler().post(new Runnable() {
1933                @Override
1934                public void run() {
1935                    callback.onConnectionEvent(call, event, extras);
1936                }
1937            });
1938        }
1939    }
1940
1941    /**
1942     * Notifies listeners of an RTT on/off change
1943     *
1944     * @param enabled True if RTT is now enabled, false otherwise
1945     */
1946    private void fireOnIsRttChanged(final boolean enabled, final RttCall rttCall) {
1947        for (CallbackRecord<Callback> record : mCallbackRecords) {
1948            final Call call = this;
1949            final Callback callback = record.getCallback();
1950            record.getHandler().post(() -> callback.onRttStatusChanged(call, enabled, rttCall));
1951        }
1952    }
1953
1954    /**
1955     * Notifies listeners of a RTT mode change
1956     *
1957     * @param mode The new RTT mode
1958     */
1959    private void fireOnRttModeChanged(final int mode) {
1960        for (CallbackRecord<Callback> record : mCallbackRecords) {
1961            final Call call = this;
1962            final Callback callback = record.getCallback();
1963            record.getHandler().post(() -> callback.onRttModeChanged(call, mode));
1964        }
1965    }
1966
1967    /**
1968     * Determines if two bundles are equal.
1969     *
1970     * @param bundle The original bundle.
1971     * @param newBundle The bundle to compare with.
1972     * @retrun {@code true} if the bundles are equal, {@code false} otherwise.
1973     */
1974    private static boolean areBundlesEqual(Bundle bundle, Bundle newBundle) {
1975        if (bundle == null || newBundle == null) {
1976            return bundle == newBundle;
1977        }
1978
1979        if (bundle.size() != newBundle.size()) {
1980            return false;
1981        }
1982
1983        for(String key : bundle.keySet()) {
1984            if (key != null) {
1985                final Object value = bundle.get(key);
1986                final Object newValue = newBundle.get(key);
1987                if (!Objects.equals(value, newValue)) {
1988                    return false;
1989                }
1990            }
1991        }
1992        return true;
1993    }
1994}
1995