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 com.android.internal.os.SomeArgs;
20import com.android.internal.telecom.IVideoCallback;
21import com.android.internal.telecom.IVideoProvider;
22
23import android.annotation.NonNull;
24import android.annotation.Nullable;
25import android.annotation.SystemApi;
26import android.hardware.camera2.CameraManager;
27import android.net.Uri;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.Looper;
32import android.os.Message;
33import android.os.RemoteException;
34import android.util.ArraySet;
35import android.view.Surface;
36
37import java.util.ArrayList;
38import java.util.Collections;
39import java.util.List;
40import java.util.Set;
41import java.util.concurrent.ConcurrentHashMap;
42
43/**
44 * Represents a phone call or connection to a remote endpoint that carries voice and/or video
45 * traffic.
46 * <p>
47 * Implementations create a custom subclass of {@code Connection} and return it to the framework
48 * as the return value of
49 * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
50 * or
51 * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
52 * Implementations are then responsible for updating the state of the {@code Connection}, and
53 * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
54 * longer used and associated resources may be recovered.
55 */
56public abstract class Connection extends Conferenceable {
57
58    /**
59     * The connection is initializing. This is generally the first state for a {@code Connection}
60     * returned by a {@link ConnectionService}.
61     */
62    public static final int STATE_INITIALIZING = 0;
63
64    /**
65     * The connection is new and not connected.
66     */
67    public static final int STATE_NEW = 1;
68
69    /**
70     * An incoming connection is in the ringing state. During this state, the user's ringer or
71     * vibration feature will be activated.
72     */
73    public static final int STATE_RINGING = 2;
74
75    /**
76     * An outgoing connection is in the dialing state. In this state the other party has not yet
77     * answered the call and the user traditionally hears a ringback tone.
78     */
79    public static final int STATE_DIALING = 3;
80
81    /**
82     * A connection is active. Both parties are connected to the call and can actively communicate.
83     */
84    public static final int STATE_ACTIVE = 4;
85
86    /**
87     * A connection is on hold.
88     */
89    public static final int STATE_HOLDING = 5;
90
91    /**
92     * A connection has been disconnected. This is the final state once the user has been
93     * disconnected from a call either locally, remotely or by an error in the service.
94     */
95    public static final int STATE_DISCONNECTED = 6;
96
97    /**
98     * The state of an external connection which is in the process of being pulled from a remote
99     * device to the local device.
100     * <p>
101     * A connection can only be in this state if the {@link #PROPERTY_IS_EXTERNAL_CALL} property and
102     * {@link #CAPABILITY_CAN_PULL_CALL} capability bits are set on the connection.
103     * @hide
104     */
105    public static final int STATE_PULLING_CALL = 7;
106
107    /**
108     * Connection can currently be put on hold or unheld. This is distinct from
109     * {@link #CAPABILITY_SUPPORT_HOLD} in that although a connection may support 'hold' most times,
110     * it does not at the moment support the function. This can be true while the call is in the
111     * state {@link #STATE_DIALING}, for example. During this condition, an in-call UI may
112     * display a disabled 'hold' button.
113     */
114    public static final int CAPABILITY_HOLD = 0x00000001;
115
116    /** Connection supports the hold feature. */
117    public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
118
119    /**
120     * Connections within a conference can be merged. A {@link ConnectionService} has the option to
121     * add a {@link Conference} before the child {@link Connection}s are merged. This is how
122     * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
123     * capability allows a merge button to be shown while the conference is in the foreground
124     * of the in-call UI.
125     * <p>
126     * This is only intended for use by a {@link Conference}.
127     */
128    public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
129
130    /**
131     * Connections within a conference can be swapped between foreground and background.
132     * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
133     * <p>
134     * This is only intended for use by a {@link Conference}.
135     */
136    public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
137
138    /**
139     * @hide
140     */
141    public static final int CAPABILITY_UNUSED = 0x00000010;
142
143    /** Connection supports responding via text option. */
144    public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
145
146    /** Connection can be muted. */
147    public static final int CAPABILITY_MUTE = 0x00000040;
148
149    /**
150     * Connection supports conference management. This capability only applies to
151     * {@link Conference}s which can have {@link Connection}s as children.
152     */
153    public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
154
155    /**
156     * Local device supports receiving video.
157     */
158    public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
159
160    /**
161     * Local device supports transmitting video.
162     */
163    public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
164
165    /**
166     * Local device supports bidirectional video calling.
167     */
168    public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
169            CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
170
171    /**
172     * Remote device supports receiving video.
173     */
174    public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
175
176    /**
177     * Remote device supports transmitting video.
178     */
179    public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
180
181    /**
182     * Remote device supports bidirectional video calling.
183     */
184    public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
185            CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
186
187    /**
188     * Connection is able to be separated from its parent {@code Conference}, if any.
189     */
190    public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
191
192    /**
193     * Connection is able to be individually disconnected when in a {@code Conference}.
194     */
195    public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
196
197    /**
198     * Un-used.
199     * @hide
200     */
201    public static final int CAPABILITY_UNUSED_2 = 0x00004000;
202
203    /**
204     * Un-used.
205     * @hide
206     */
207    public static final int CAPABILITY_UNUSED_3 = 0x00008000;
208
209    /**
210     * Un-used.
211     * @hide
212     */
213    public static final int CAPABILITY_UNUSED_4 = 0x00010000;
214
215    /**
216     * Un-used.
217     * @hide
218     */
219    public static final int CAPABILITY_UNUSED_5 = 0x00020000;
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     */
230    public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
231
232    /**
233     * For video calls, indicates whether the outgoing video for the call can be paused using
234     * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
235     */
236    public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
237
238    /**
239     * For a conference, indicates the conference will not have child connections.
240     * <p>
241     * An example of a conference with child connections is a GSM conference call, where the radio
242     * retains connections to the individual participants of the conference.  Another example is an
243     * IMS conference call where conference event package functionality is supported; in this case
244     * the conference server ensures the radio is aware of the participants in the conference, which
245     * are represented by child connections.
246     * <p>
247     * An example of a conference with no child connections is an IMS conference call with no
248     * conference event package support.  Such a conference is represented by the radio as a single
249     * connection to the IMS conference server.
250     * <p>
251     * Indicating whether a conference has children or not is important to help user interfaces
252     * visually represent a conference.  A conference with no children, for example, will have the
253     * conference connection shown in the list of calls on a Bluetooth device, where if the
254     * conference has children, only the children will be shown in the list of calls on a Bluetooth
255     * device.
256     * @hide
257     */
258    public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 0x00200000;
259
260    /**
261     * Indicates that the connection itself wants to handle any sort of reply response, rather than
262     * relying on SMS.
263     */
264    public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
265
266    /**
267     * When set, prevents a video call from being downgraded to an audio-only call.
268     * <p>
269     * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
270     * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
271     * downgraded from a video call back to a VideoState of
272     * {@link VideoProfile#STATE_AUDIO_ONLY}.
273     * <p>
274     * Intuitively, a call which can be downgraded to audio should also have local and remote
275     * video
276     * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
277     * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
278     */
279    public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
280
281    /**
282     * When set for an external connection, indicates that this {@code Connection} can be pulled
283     * from a remote device to the current device.
284     * <p>
285     * Should only be set on a {@code Connection} where {@link #PROPERTY_IS_EXTERNAL_CALL}
286     * is set.
287     * @hide
288     */
289    public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
290
291    //**********************************************************************************************
292    // Next CAPABILITY value: 0x02000000
293    //**********************************************************************************************
294
295    /**
296     * Indicates that the current device callback number should be shown.
297     *
298     * @hide
299     */
300    public static final int PROPERTY_SHOW_CALLBACK_NUMBER = 1<<0;
301
302    /**
303     * Whether the call is a generic conference, where we do not know the precise state of
304     * participants in the conference (eg. on CDMA).
305     *
306     * @hide
307     */
308    public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1;
309
310    /**
311     * Connection is using high definition audio.
312     * @hide
313     */
314    public static final int PROPERTY_HIGH_DEF_AUDIO = 1<<2;
315
316    /**
317     * Connection is using WIFI.
318     * @hide
319     */
320    public static final int PROPERTY_WIFI = 1<<3;
321
322    /**
323     * When set, indicates that the {@code Connection} does not actually exist locally for the
324     * {@link ConnectionService}.
325     * <p>
326     * Consider, for example, a scenario where a user has two devices with the same phone number.
327     * When a user places a call on one devices, the telephony stack can represent that call on the
328     * other device by adding is to the {@link ConnectionService} with the
329     * {@link #PROPERTY_IS_EXTERNAL_CALL} capability set.
330     * <p>
331     * An {@link ConnectionService} should not assume that all {@link InCallService}s will handle
332     * external connections.  Only those {@link InCallService}s which have the
333     * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
334     * manifest will see external connections.
335     * @hide
336     */
337    public static final int PROPERTY_IS_EXTERNAL_CALL = 1<<4;
338
339
340    //**********************************************************************************************
341    // Next PROPERTY value: 1<<5
342    //**********************************************************************************************
343
344    /**
345     * Connection extra key used to store the last forwarded number associated with the current
346     * connection.  Used to communicate to the user interface that the connection was forwarded via
347     * the specified number.
348     */
349    public static final String EXTRA_LAST_FORWARDED_NUMBER =
350            "android.telecom.extra.LAST_FORWARDED_NUMBER";
351
352    /**
353     * Connection extra key used to store a child number associated with the current connection.
354     * Used to communicate to the user interface that the connection was received via
355     * a child address (i.e. phone number) associated with the {@link PhoneAccount}'s primary
356     * address.
357     */
358    public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
359
360    /**
361     * Connection extra key used to store the subject for an incoming call.  The user interface can
362     * query this extra and display its contents for incoming calls.  Will only be used if the
363     * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT}.
364     */
365    public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
366
367    /**
368     * Connection event used to inform Telecom that it should play the on hold tone.  This is used
369     * to play a tone when the peer puts the current call on hold.  Sent to Telecom via
370     * {@link #sendConnectionEvent(String)}.
371     * @hide
372     */
373    public static final String EVENT_ON_HOLD_TONE_START =
374            "android.telecom.event.ON_HOLD_TONE_START";
375
376    /**
377     * Connection event used to inform Telecom that it should stop the on hold tone.  This is used
378     * to stop a tone when the peer puts the current call on hold.  Sent to Telecom via
379     * {@link #sendConnectionEvent(String)}.
380     * @hide
381     */
382    public static final String EVENT_ON_HOLD_TONE_END =
383            "android.telecom.event.ON_HOLD_TONE_END";
384
385    /**
386     * Connection event used to inform {@link InCallService}s when pulling of an external call has
387     * failed.  The user interface should inform the user of the error.
388     * <p>
389     * Expected to be used by the {@link ConnectionService} when the {@link Call#pullExternalCall()}
390     * API is called on a {@link Call} with the properties
391     * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} and
392     * {@link Call.Details#CAPABILITY_CAN_PULL_CALL}, but the {@link ConnectionService} could not
393     * pull the external call due to an error condition.
394     * @hide
395     */
396    public static final String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
397
398    // Flag controlling whether PII is emitted into the logs
399    private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
400
401    /**
402     * Whether the given capabilities support the specified capability.
403     *
404     * @param capabilities A capability bit field.
405     * @param capability The capability to check capabilities for.
406     * @return Whether the specified capability is supported.
407     * @hide
408     */
409    public static boolean can(int capabilities, int capability) {
410        return (capabilities & capability) == capability;
411    }
412
413    /**
414     * Whether the capabilities of this {@code Connection} supports the specified capability.
415     *
416     * @param capability The capability to check capabilities for.
417     * @return Whether the specified capability is supported.
418     * @hide
419     */
420    public boolean can(int capability) {
421        return can(mConnectionCapabilities, capability);
422    }
423
424    /**
425     * Removes the specified capability from the set of capabilities of this {@code Connection}.
426     *
427     * @param capability The capability to remove from the set.
428     * @hide
429     */
430    public void removeCapability(int capability) {
431        mConnectionCapabilities &= ~capability;
432    }
433
434    /**
435     * Adds the specified capability to the set of capabilities of this {@code Connection}.
436     *
437     * @param capability The capability to add to the set.
438     * @hide
439     */
440    public void addCapability(int capability) {
441        mConnectionCapabilities |= capability;
442    }
443
444
445    public static String capabilitiesToString(int capabilities) {
446        StringBuilder builder = new StringBuilder();
447        builder.append("[Capabilities:");
448        if (can(capabilities, CAPABILITY_HOLD)) {
449            builder.append(" CAPABILITY_HOLD");
450        }
451        if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
452            builder.append(" CAPABILITY_SUPPORT_HOLD");
453        }
454        if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
455            builder.append(" CAPABILITY_MERGE_CONFERENCE");
456        }
457        if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
458            builder.append(" CAPABILITY_SWAP_CONFERENCE");
459        }
460        if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
461            builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
462        }
463        if (can(capabilities, CAPABILITY_MUTE)) {
464            builder.append(" CAPABILITY_MUTE");
465        }
466        if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
467            builder.append(" CAPABILITY_MANAGE_CONFERENCE");
468        }
469        if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
470            builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
471        }
472        if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
473            builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
474        }
475        if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
476            builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
477        }
478        if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
479            builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
480        }
481        if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
482            builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
483        }
484        if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
485            builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
486        }
487        if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
488            builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
489        }
490        if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
491            builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
492        }
493        if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
494            builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
495        }
496        if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
497            builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
498        }
499        if (can(capabilities, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
500            builder.append(" CAPABILITY_SINGLE_PARTY_CONFERENCE");
501        }
502        if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
503            builder.append(" CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION");
504        }
505        if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
506            builder.append(" CAPABILITY_CAN_PULL_CALL");
507        }
508
509        builder.append("]");
510        return builder.toString();
511    }
512
513    /**
514     * Builds a string representation of a properties bit-mask.
515     *
516     * @param properties The properties bit-mask.
517     * @return String representation.
518     * @hide
519     */
520    public static String propertiesToString(int properties) {
521        StringBuilder builder = new StringBuilder();
522        builder.append("[Properties:");
523
524        if (can(properties, PROPERTY_SHOW_CALLBACK_NUMBER)) {
525            builder.append(" PROPERTY_SHOW_CALLBACK_NUMBER");
526        }
527
528        if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
529            builder.append(" PROPERTY_HIGH_DEF_AUDIO");
530        }
531
532        if (can(properties, PROPERTY_WIFI)) {
533            builder.append(" PROPERTY_WIFI");
534        }
535
536        if (can(properties, PROPERTY_GENERIC_CONFERENCE)) {
537            builder.append(" PROPERTY_GENERIC_CONFERENCE");
538        }
539
540        if (can(properties, PROPERTY_IS_EXTERNAL_CALL)) {
541            builder.append(" PROPERTY_IS_EXTERNAL_CALL");
542        }
543
544        builder.append("]");
545        return builder.toString();
546    }
547
548    /** @hide */
549    public abstract static class Listener {
550        public void onStateChanged(Connection c, int state) {}
551        public void onAddressChanged(Connection c, Uri newAddress, int presentation) {}
552        public void onCallerDisplayNameChanged(
553                Connection c, String callerDisplayName, int presentation) {}
554        public void onVideoStateChanged(Connection c, int videoState) {}
555        public void onDisconnected(Connection c, DisconnectCause disconnectCause) {}
556        public void onPostDialWait(Connection c, String remaining) {}
557        public void onPostDialChar(Connection c, char nextChar) {}
558        public void onRingbackRequested(Connection c, boolean ringback) {}
559        public void onDestroyed(Connection c) {}
560        public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {}
561        public void onConnectionPropertiesChanged(Connection c, int properties) {}
562        public void onVideoProviderChanged(
563                Connection c, VideoProvider videoProvider) {}
564        public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
565        public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
566        public void onConferenceablesChanged(
567                Connection c, List<Conferenceable> conferenceables) {}
568        public void onConferenceChanged(Connection c, Conference conference) {}
569        /** @hide */
570        public void onConferenceParticipantsChanged(Connection c,
571                List<ConferenceParticipant> participants) {}
572        public void onConferenceStarted() {}
573        public void onConferenceMergeFailed(Connection c) {}
574        public void onExtrasChanged(Connection c, Bundle extras) {}
575        public void onExtrasRemoved(Connection c, List<String> keys) {}
576        public void onConnectionEvent(Connection c, String event, Bundle extras) {}
577    }
578
579    /**
580     * Provides a means of controlling the video session associated with a {@link Connection}.
581     * <p>
582     * Implementations create a custom subclass of {@link VideoProvider} and the
583     * {@link ConnectionService} creates an instance sets it on the {@link Connection} using
584     * {@link Connection#setVideoProvider(VideoProvider)}.  Any connection which supports video
585     * should set the {@link VideoProvider}.
586     * <p>
587     * The {@link VideoProvider} serves two primary purposes: it provides a means for Telecom and
588     * {@link InCallService} implementations to issue requests related to the video session;
589     * it provides a means for the {@link ConnectionService} to report events and information
590     * related to the video session to Telecom and the {@link InCallService} implementations.
591     * <p>
592     * {@link InCallService} implementations interact with the {@link VideoProvider} via
593     * {@link android.telecom.InCallService.VideoCall}.
594     */
595    public static abstract class VideoProvider {
596
597        /**
598         * Video is not being received (no protocol pause was issued).
599         * @see #handleCallSessionEvent(int)
600         */
601        public static final int SESSION_EVENT_RX_PAUSE = 1;
602
603        /**
604         * Video reception has resumed after a {@link #SESSION_EVENT_RX_PAUSE}.
605         * @see #handleCallSessionEvent(int)
606         */
607        public static final int SESSION_EVENT_RX_RESUME = 2;
608
609        /**
610         * Video transmission has begun. This occurs after a negotiated start of video transmission
611         * when the underlying protocol has actually begun transmitting video to the remote party.
612         * @see #handleCallSessionEvent(int)
613         */
614        public static final int SESSION_EVENT_TX_START = 3;
615
616        /**
617         * Video transmission has stopped. This occurs after a negotiated stop of video transmission
618         * when the underlying protocol has actually stopped transmitting video to the remote party.
619         * @see #handleCallSessionEvent(int)
620         */
621        public static final int SESSION_EVENT_TX_STOP = 4;
622
623        /**
624         * A camera failure has occurred for the selected camera.  The {@link InCallService} can use
625         * this as a cue to inform the user the camera is not available.
626         * @see #handleCallSessionEvent(int)
627         */
628        public static final int SESSION_EVENT_CAMERA_FAILURE = 5;
629
630        /**
631         * Issued after {@link #SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready
632         * for operation.  The {@link InCallService} can use this as a cue to inform the user that
633         * the camera has become available again.
634         * @see #handleCallSessionEvent(int)
635         */
636        public static final int SESSION_EVENT_CAMERA_READY = 6;
637
638        /**
639         * Session modify request was successful.
640         * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
641         */
642        public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
643
644        /**
645         * Session modify request failed.
646         * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
647         */
648        public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
649
650        /**
651         * Session modify request ignored due to invalid parameters.
652         * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
653         */
654        public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
655
656        /**
657         * Session modify request timed out.
658         * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
659         */
660        public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
661
662        /**
663         * Session modify request rejected by remote user.
664         * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
665         */
666        public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
667
668        private static final int MSG_ADD_VIDEO_CALLBACK = 1;
669        private static final int MSG_SET_CAMERA = 2;
670        private static final int MSG_SET_PREVIEW_SURFACE = 3;
671        private static final int MSG_SET_DISPLAY_SURFACE = 4;
672        private static final int MSG_SET_DEVICE_ORIENTATION = 5;
673        private static final int MSG_SET_ZOOM = 6;
674        private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
675        private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
676        private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
677        private static final int MSG_REQUEST_CONNECTION_DATA_USAGE = 10;
678        private static final int MSG_SET_PAUSE_IMAGE = 11;
679        private static final int MSG_REMOVE_VIDEO_CALLBACK = 12;
680
681        private VideoProvider.VideoProviderHandler mMessageHandler;
682        private final VideoProvider.VideoProviderBinder mBinder;
683
684        /**
685         * Stores a list of the video callbacks, keyed by IBinder.
686         *
687         * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
688         * load factor before resizing, 1 means we only expect a single thread to
689         * access the map so make only a single shard
690         */
691        private ConcurrentHashMap<IBinder, IVideoCallback> mVideoCallbacks =
692                new ConcurrentHashMap<IBinder, IVideoCallback>(8, 0.9f, 1);
693
694        /**
695         * Default handler used to consolidate binder method calls onto a single thread.
696         */
697        private final class VideoProviderHandler extends Handler {
698            public VideoProviderHandler() {
699                super();
700            }
701
702            public VideoProviderHandler(Looper looper) {
703                super(looper);
704            }
705
706            @Override
707            public void handleMessage(Message msg) {
708                switch (msg.what) {
709                    case MSG_ADD_VIDEO_CALLBACK: {
710                        IBinder binder = (IBinder) msg.obj;
711                        IVideoCallback callback = IVideoCallback.Stub
712                                .asInterface((IBinder) msg.obj);
713                        if (callback == null) {
714                            Log.w(this, "addVideoProvider - skipped; callback is null.");
715                            break;
716                        }
717
718                        if (mVideoCallbacks.containsKey(binder)) {
719                            Log.i(this, "addVideoProvider - skipped; already present.");
720                            break;
721                        }
722                        mVideoCallbacks.put(binder, callback);
723                        break;
724                    }
725                    case MSG_REMOVE_VIDEO_CALLBACK: {
726                        IBinder binder = (IBinder) msg.obj;
727                        IVideoCallback callback = IVideoCallback.Stub
728                                .asInterface((IBinder) msg.obj);
729                        if (!mVideoCallbacks.containsKey(binder)) {
730                            Log.i(this, "removeVideoProvider - skipped; not present.");
731                            break;
732                        }
733                        mVideoCallbacks.remove(binder);
734                        break;
735                    }
736                    case MSG_SET_CAMERA:
737                        onSetCamera((String) msg.obj);
738                        break;
739                    case MSG_SET_PREVIEW_SURFACE:
740                        onSetPreviewSurface((Surface) msg.obj);
741                        break;
742                    case MSG_SET_DISPLAY_SURFACE:
743                        onSetDisplaySurface((Surface) msg.obj);
744                        break;
745                    case MSG_SET_DEVICE_ORIENTATION:
746                        onSetDeviceOrientation(msg.arg1);
747                        break;
748                    case MSG_SET_ZOOM:
749                        onSetZoom((Float) msg.obj);
750                        break;
751                    case MSG_SEND_SESSION_MODIFY_REQUEST: {
752                        SomeArgs args = (SomeArgs) msg.obj;
753                        try {
754                            onSendSessionModifyRequest((VideoProfile) args.arg1,
755                                    (VideoProfile) args.arg2);
756                        } finally {
757                            args.recycle();
758                        }
759                        break;
760                    }
761                    case MSG_SEND_SESSION_MODIFY_RESPONSE:
762                        onSendSessionModifyResponse((VideoProfile) msg.obj);
763                        break;
764                    case MSG_REQUEST_CAMERA_CAPABILITIES:
765                        onRequestCameraCapabilities();
766                        break;
767                    case MSG_REQUEST_CONNECTION_DATA_USAGE:
768                        onRequestConnectionDataUsage();
769                        break;
770                    case MSG_SET_PAUSE_IMAGE:
771                        onSetPauseImage((Uri) msg.obj);
772                        break;
773                    default:
774                        break;
775                }
776            }
777        }
778
779        /**
780         * IVideoProvider stub implementation.
781         */
782        private final class VideoProviderBinder extends IVideoProvider.Stub {
783            public void addVideoCallback(IBinder videoCallbackBinder) {
784                mMessageHandler.obtainMessage(
785                        MSG_ADD_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
786            }
787
788            public void removeVideoCallback(IBinder videoCallbackBinder) {
789                mMessageHandler.obtainMessage(
790                        MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
791            }
792
793            public void setCamera(String cameraId) {
794                mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
795            }
796
797            public void setPreviewSurface(Surface surface) {
798                mMessageHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
799            }
800
801            public void setDisplaySurface(Surface surface) {
802                mMessageHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
803            }
804
805            public void setDeviceOrientation(int rotation) {
806                mMessageHandler.obtainMessage(
807                        MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
808            }
809
810            public void setZoom(float value) {
811                mMessageHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
812            }
813
814            public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
815                SomeArgs args = SomeArgs.obtain();
816                args.arg1 = fromProfile;
817                args.arg2 = toProfile;
818                mMessageHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
819            }
820
821            public void sendSessionModifyResponse(VideoProfile responseProfile) {
822                mMessageHandler.obtainMessage(
823                        MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
824            }
825
826            public void requestCameraCapabilities() {
827                mMessageHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
828            }
829
830            public void requestCallDataUsage() {
831                mMessageHandler.obtainMessage(MSG_REQUEST_CONNECTION_DATA_USAGE).sendToTarget();
832            }
833
834            public void setPauseImage(Uri uri) {
835                mMessageHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
836            }
837        }
838
839        public VideoProvider() {
840            mBinder = new VideoProvider.VideoProviderBinder();
841            mMessageHandler = new VideoProvider.VideoProviderHandler(Looper.getMainLooper());
842        }
843
844        /**
845         * Creates an instance of the {@link VideoProvider}, specifying the looper to use.
846         *
847         * @param looper The looper.
848         * @hide
849         */
850        public VideoProvider(Looper looper) {
851            mBinder = new VideoProvider.VideoProviderBinder();
852            mMessageHandler = new VideoProvider.VideoProviderHandler(looper);
853        }
854
855        /**
856         * Returns binder object which can be used across IPC methods.
857         * @hide
858         */
859        public final IVideoProvider getInterface() {
860            return mBinder;
861        }
862
863        /**
864         * Sets the camera to be used for the outgoing video.
865         * <p>
866         * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
867         * camera via
868         * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
869         * <p>
870         * Sent from the {@link InCallService} via
871         * {@link InCallService.VideoCall#setCamera(String)}.
872         *
873         * @param cameraId The id of the camera (use ids as reported by
874         * {@link CameraManager#getCameraIdList()}).
875         */
876        public abstract void onSetCamera(String cameraId);
877
878        /**
879         * Sets the surface to be used for displaying a preview of what the user's camera is
880         * currently capturing.  When video transmission is enabled, this is the video signal which
881         * is sent to the remote device.
882         * <p>
883         * Sent from the {@link InCallService} via
884         * {@link InCallService.VideoCall#setPreviewSurface(Surface)}.
885         *
886         * @param surface The {@link Surface}.
887         */
888        public abstract void onSetPreviewSurface(Surface surface);
889
890        /**
891         * Sets the surface to be used for displaying the video received from the remote device.
892         * <p>
893         * Sent from the {@link InCallService} via
894         * {@link InCallService.VideoCall#setDisplaySurface(Surface)}.
895         *
896         * @param surface The {@link Surface}.
897         */
898        public abstract void onSetDisplaySurface(Surface surface);
899
900        /**
901         * Sets the device orientation, in degrees.  Assumes that a standard portrait orientation of
902         * the device is 0 degrees.
903         * <p>
904         * Sent from the {@link InCallService} via
905         * {@link InCallService.VideoCall#setDeviceOrientation(int)}.
906         *
907         * @param rotation The device orientation, in degrees.
908         */
909        public abstract void onSetDeviceOrientation(int rotation);
910
911        /**
912         * Sets camera zoom ratio.
913         * <p>
914         * Sent from the {@link InCallService} via {@link InCallService.VideoCall#setZoom(float)}.
915         *
916         * @param value The camera zoom ratio.
917         */
918        public abstract void onSetZoom(float value);
919
920        /**
921         * Issues a request to modify the properties of the current video session.
922         * <p>
923         * Example scenarios include: requesting an audio-only call to be upgraded to a
924         * bi-directional video call, turning on or off the user's camera, sending a pause signal
925         * when the {@link InCallService} is no longer the foreground application.
926         * <p>
927         * If the {@link VideoProvider} determines a request to be invalid, it should call
928         * {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)} to report the
929         * invalid request back to the {@link InCallService}.
930         * <p>
931         * Where a request requires confirmation from the user of the peer device, the
932         * {@link VideoProvider} must communicate the request to the peer device and handle the
933         * user's response.  {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)}
934         * is used to inform the {@link InCallService} of the result of the request.
935         * <p>
936         * Sent from the {@link InCallService} via
937         * {@link InCallService.VideoCall#sendSessionModifyRequest(VideoProfile)}.
938         *
939         * @param fromProfile The video profile prior to the request.
940         * @param toProfile The video profile with the requested changes made.
941         */
942        public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
943                VideoProfile toProfile);
944
945        /**
946         * Provides a response to a request to change the current video session properties.
947         * <p>
948         * For example, if the peer requests and upgrade from an audio-only call to a bi-directional
949         * video call, could decline the request and keep the call as audio-only.
950         * In such a scenario, the {@code responseProfile} would have a video state of
951         * {@link VideoProfile#STATE_AUDIO_ONLY}.  If the user had decided to accept the request,
952         * the video state would be {@link VideoProfile#STATE_BIDIRECTIONAL}.
953         * <p>
954         * Sent from the {@link InCallService} via
955         * {@link InCallService.VideoCall#sendSessionModifyResponse(VideoProfile)} in response to
956         * a {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)}
957         * callback.
958         *
959         * @param responseProfile The response video profile.
960         */
961        public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
962
963        /**
964         * Issues a request to the {@link VideoProvider} to retrieve the camera capabilities.
965         * <p>
966         * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
967         * camera via
968         * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
969         * <p>
970         * Sent from the {@link InCallService} via
971         * {@link InCallService.VideoCall#requestCameraCapabilities()}.
972         */
973        public abstract void onRequestCameraCapabilities();
974
975        /**
976         * Issues a request to the {@link VideoProvider} to retrieve the current data usage for the
977         * video component of the current {@link Connection}.
978         * <p>
979         * The {@link VideoProvider} should respond by communicating current data usage, in bytes,
980         * via {@link VideoProvider#setCallDataUsage(long)}.
981         * <p>
982         * Sent from the {@link InCallService} via
983         * {@link InCallService.VideoCall#requestCallDataUsage()}.
984         */
985        public abstract void onRequestConnectionDataUsage();
986
987        /**
988         * Provides the {@link VideoProvider} with the {@link Uri} of an image to be displayed to
989         * the peer device when the video signal is paused.
990         * <p>
991         * Sent from the {@link InCallService} via
992         * {@link InCallService.VideoCall#setPauseImage(Uri)}.
993         *
994         * @param uri URI of image to display.
995         */
996        public abstract void onSetPauseImage(Uri uri);
997
998        /**
999         * Used to inform listening {@link InCallService} implementations when the
1000         * {@link VideoProvider} receives a session modification request.
1001         * <p>
1002         * Received by the {@link InCallService} via
1003         * {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)},
1004         *
1005         * @param videoProfile The requested video profile.
1006         * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
1007         */
1008        public void receiveSessionModifyRequest(VideoProfile videoProfile) {
1009            if (mVideoCallbacks != null) {
1010                for (IVideoCallback callback : mVideoCallbacks.values()) {
1011                    try {
1012                        callback.receiveSessionModifyRequest(videoProfile);
1013                    } catch (RemoteException ignored) {
1014                        Log.w(this, "receiveSessionModifyRequest callback failed", ignored);
1015                    }
1016                }
1017            }
1018        }
1019
1020        /**
1021         * Used to inform listening {@link InCallService} implementations when the
1022         * {@link VideoProvider} receives a response to a session modification request.
1023         * <p>
1024         * Received by the {@link InCallService} via
1025         * {@link InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
1026         * VideoProfile, VideoProfile)}.
1027         *
1028         * @param status Status of the session modify request.  Valid values are
1029         *               {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
1030         *               {@link VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
1031         *               {@link VideoProvider#SESSION_MODIFY_REQUEST_INVALID},
1032         *               {@link VideoProvider#SESSION_MODIFY_REQUEST_TIMED_OUT},
1033         *               {@link VideoProvider#SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE}
1034         * @param requestedProfile The original request which was sent to the peer device.
1035         * @param responseProfile The actual profile changes agreed to by the peer device.
1036         * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
1037         */
1038        public void receiveSessionModifyResponse(int status,
1039                VideoProfile requestedProfile, VideoProfile responseProfile) {
1040            if (mVideoCallbacks != null) {
1041                for (IVideoCallback callback : mVideoCallbacks.values()) {
1042                    try {
1043                        callback.receiveSessionModifyResponse(status, requestedProfile,
1044                                responseProfile);
1045                    } catch (RemoteException ignored) {
1046                        Log.w(this, "receiveSessionModifyResponse callback failed", ignored);
1047                    }
1048                }
1049            }
1050        }
1051
1052        /**
1053         * Used to inform listening {@link InCallService} implementations when the
1054         * {@link VideoProvider} reports a call session event.
1055         * <p>
1056         * Received by the {@link InCallService} via
1057         * {@link InCallService.VideoCall.Callback#onCallSessionEvent(int)}.
1058         *
1059         * @param event The event.  Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
1060         *      {@link VideoProvider#SESSION_EVENT_RX_RESUME},
1061         *      {@link VideoProvider#SESSION_EVENT_TX_START},
1062         *      {@link VideoProvider#SESSION_EVENT_TX_STOP},
1063         *      {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
1064         *      {@link VideoProvider#SESSION_EVENT_CAMERA_READY}.
1065         */
1066        public void handleCallSessionEvent(int event) {
1067            if (mVideoCallbacks != null) {
1068                for (IVideoCallback callback : mVideoCallbacks.values()) {
1069                    try {
1070                        callback.handleCallSessionEvent(event);
1071                    } catch (RemoteException ignored) {
1072                        Log.w(this, "handleCallSessionEvent callback failed", ignored);
1073                    }
1074                }
1075            }
1076        }
1077
1078        /**
1079         * Used to inform listening {@link InCallService} implementations when the dimensions of the
1080         * peer's video have changed.
1081         * <p>
1082         * This could occur if, for example, the peer rotates their device, changing the aspect
1083         * ratio of the video, or if the user switches between the back and front cameras.
1084         * <p>
1085         * Received by the {@link InCallService} via
1086         * {@link InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)}.
1087         *
1088         * @param width  The updated peer video width.
1089         * @param height The updated peer video height.
1090         */
1091        public void changePeerDimensions(int width, int height) {
1092            if (mVideoCallbacks != null) {
1093                for (IVideoCallback callback : mVideoCallbacks.values()) {
1094                    try {
1095                        callback.changePeerDimensions(width, height);
1096                    } catch (RemoteException ignored) {
1097                        Log.w(this, "changePeerDimensions callback failed", ignored);
1098                    }
1099                }
1100            }
1101        }
1102
1103        /**
1104         * Used to inform listening {@link InCallService} implementations when the data usage of the
1105         * video associated with the current {@link Connection} has changed.
1106         * <p>
1107         * This could be in response to a preview request via
1108         * {@link #onRequestConnectionDataUsage()}, or as a periodic update by the
1109         * {@link VideoProvider}.  Where periodic updates of data usage are provided, they should be
1110         * provided at most for every 1 MB of data transferred and no more than once every 10 sec.
1111         * <p>
1112         * Received by the {@link InCallService} via
1113         * {@link InCallService.VideoCall.Callback#onCallDataUsageChanged(long)}.
1114         *
1115         * @param dataUsage The updated data usage (in bytes).  Reported as the cumulative bytes
1116         *                  used since the start of the call.
1117         */
1118        public void setCallDataUsage(long dataUsage) {
1119            if (mVideoCallbacks != null) {
1120                for (IVideoCallback callback : mVideoCallbacks.values()) {
1121                    try {
1122                        callback.changeCallDataUsage(dataUsage);
1123                    } catch (RemoteException ignored) {
1124                        Log.w(this, "setCallDataUsage callback failed", ignored);
1125                    }
1126                }
1127            }
1128        }
1129
1130        /**
1131         * @see #setCallDataUsage(long)
1132         *
1133         * @param dataUsage The updated data usage (in byes).
1134         * @deprecated - Use {@link #setCallDataUsage(long)} instead.
1135         * @hide
1136         */
1137        public void changeCallDataUsage(long dataUsage) {
1138            setCallDataUsage(dataUsage);
1139        }
1140
1141        /**
1142         * Used to inform listening {@link InCallService} implementations when the capabilities of
1143         * the current camera have changed.
1144         * <p>
1145         * The {@link VideoProvider} should call this in response to
1146         * {@link VideoProvider#onRequestCameraCapabilities()}, or when the current camera is
1147         * changed via {@link VideoProvider#onSetCamera(String)}.
1148         * <p>
1149         * Received by the {@link InCallService} via
1150         * {@link InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
1151         * VideoProfile.CameraCapabilities)}.
1152         *
1153         * @param cameraCapabilities The new camera capabilities.
1154         */
1155        public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
1156            if (mVideoCallbacks != null) {
1157                for (IVideoCallback callback : mVideoCallbacks.values()) {
1158                    try {
1159                        callback.changeCameraCapabilities(cameraCapabilities);
1160                    } catch (RemoteException ignored) {
1161                        Log.w(this, "changeCameraCapabilities callback failed", ignored);
1162                    }
1163                }
1164            }
1165        }
1166
1167        /**
1168         * Used to inform listening {@link InCallService} implementations when the video quality
1169         * of the call has changed.
1170         * <p>
1171         * Received by the {@link InCallService} via
1172         * {@link InCallService.VideoCall.Callback#onVideoQualityChanged(int)}.
1173         *
1174         * @param videoQuality The updated video quality.  Valid values:
1175         *      {@link VideoProfile#QUALITY_HIGH},
1176         *      {@link VideoProfile#QUALITY_MEDIUM},
1177         *      {@link VideoProfile#QUALITY_LOW},
1178         *      {@link VideoProfile#QUALITY_DEFAULT}.
1179         */
1180        public void changeVideoQuality(int videoQuality) {
1181            if (mVideoCallbacks != null) {
1182                for (IVideoCallback callback : mVideoCallbacks.values()) {
1183                    try {
1184                        callback.changeVideoQuality(videoQuality);
1185                    } catch (RemoteException ignored) {
1186                        Log.w(this, "changeVideoQuality callback failed", ignored);
1187                    }
1188                }
1189            }
1190        }
1191    }
1192
1193    private final Listener mConnectionDeathListener = new Listener() {
1194        @Override
1195        public void onDestroyed(Connection c) {
1196            if (mConferenceables.remove(c)) {
1197                fireOnConferenceableConnectionsChanged();
1198            }
1199        }
1200    };
1201
1202    private final Conference.Listener mConferenceDeathListener = new Conference.Listener() {
1203        @Override
1204        public void onDestroyed(Conference c) {
1205            if (mConferenceables.remove(c)) {
1206                fireOnConferenceableConnectionsChanged();
1207            }
1208        }
1209    };
1210
1211    /**
1212     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
1213     * load factor before resizing, 1 means we only expect a single thread to
1214     * access the map so make only a single shard
1215     */
1216    private final Set<Listener> mListeners = Collections.newSetFromMap(
1217            new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
1218    private final List<Conferenceable> mConferenceables = new ArrayList<>();
1219    private final List<Conferenceable> mUnmodifiableConferenceables =
1220            Collections.unmodifiableList(mConferenceables);
1221
1222    // The internal telecom call ID associated with this connection.
1223    private String mTelecomCallId;
1224    private int mState = STATE_NEW;
1225    private CallAudioState mCallAudioState;
1226    private Uri mAddress;
1227    private int mAddressPresentation;
1228    private String mCallerDisplayName;
1229    private int mCallerDisplayNamePresentation;
1230    private boolean mRingbackRequested = false;
1231    private int mConnectionCapabilities;
1232    private int mConnectionProperties;
1233    private VideoProvider mVideoProvider;
1234    private boolean mAudioModeIsVoip;
1235    private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
1236    private StatusHints mStatusHints;
1237    private int mVideoState;
1238    private DisconnectCause mDisconnectCause;
1239    private Conference mConference;
1240    private ConnectionService mConnectionService;
1241    private Bundle mExtras;
1242    private final Object mExtrasLock = new Object();
1243
1244    /**
1245     * Tracks the key set for the extras bundle provided on the last invocation of
1246     * {@link #setExtras(Bundle)}.  Used so that on subsequent invocations we can remove any extras
1247     * keys which were set previously but are no longer present in the replacement Bundle.
1248     */
1249    private Set<String> mPreviousExtraKeys;
1250
1251    /**
1252     * Create a new Connection.
1253     */
1254    public Connection() {}
1255
1256    /**
1257     * Returns the Telecom internal call ID associated with this connection.  Should only be used
1258     * for debugging and tracing purposes.
1259     *
1260     * @return The Telecom call ID.
1261     * @hide
1262     */
1263    public final String getTelecomCallId() {
1264        return mTelecomCallId;
1265    }
1266
1267    /**
1268     * @return The address (e.g., phone number) to which this Connection is currently communicating.
1269     */
1270    public final Uri getAddress() {
1271        return mAddress;
1272    }
1273
1274    /**
1275     * @return The presentation requirements for the address.
1276     *         See {@link TelecomManager} for valid values.
1277     */
1278    public final int getAddressPresentation() {
1279        return mAddressPresentation;
1280    }
1281
1282    /**
1283     * @return The caller display name (CNAP).
1284     */
1285    public final String getCallerDisplayName() {
1286        return mCallerDisplayName;
1287    }
1288
1289    /**
1290     * @return The presentation requirements for the handle.
1291     *         See {@link TelecomManager} for valid values.
1292     */
1293    public final int getCallerDisplayNamePresentation() {
1294        return mCallerDisplayNamePresentation;
1295    }
1296
1297    /**
1298     * @return The state of this Connection.
1299     */
1300    public final int getState() {
1301        return mState;
1302    }
1303
1304    /**
1305     * Returns the video state of the connection.
1306     * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
1307     * {@link VideoProfile#STATE_BIDIRECTIONAL},
1308     * {@link VideoProfile#STATE_TX_ENABLED},
1309     * {@link VideoProfile#STATE_RX_ENABLED}.
1310     *
1311     * @return The video state of the connection.
1312     * @hide
1313     */
1314    public final int getVideoState() {
1315        return mVideoState;
1316    }
1317
1318    /**
1319     * @return The audio state of the connection, describing how its audio is currently
1320     *         being routed by the system. This is {@code null} if this Connection
1321     *         does not directly know about its audio state.
1322     * @deprecated Use {@link #getCallAudioState()} instead.
1323     * @hide
1324     */
1325    @SystemApi
1326    @Deprecated
1327    public final AudioState getAudioState() {
1328        if (mCallAudioState == null) {
1329          return null;
1330        }
1331        return new AudioState(mCallAudioState);
1332    }
1333
1334    /**
1335     * @return The audio state of the connection, describing how its audio is currently
1336     *         being routed by the system. This is {@code null} if this Connection
1337     *         does not directly know about its audio state.
1338     */
1339    public final CallAudioState getCallAudioState() {
1340        return mCallAudioState;
1341    }
1342
1343    /**
1344     * @return The conference that this connection is a part of.  Null if it is not part of any
1345     *         conference.
1346     */
1347    public final Conference getConference() {
1348        return mConference;
1349    }
1350
1351    /**
1352     * Returns whether this connection is requesting that the system play a ringback tone
1353     * on its behalf.
1354     */
1355    public final boolean isRingbackRequested() {
1356        return mRingbackRequested;
1357    }
1358
1359    /**
1360     * @return True if the connection's audio mode is VOIP.
1361     */
1362    public final boolean getAudioModeIsVoip() {
1363        return mAudioModeIsVoip;
1364    }
1365
1366    /**
1367     * Retrieves the connection start time of the {@code Connnection}, if specified.  A value of
1368     * {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the
1369     * start time of the conference.
1370     *
1371     * @return The time at which the {@code Connnection} was connected.
1372     *
1373     * @hide
1374     */
1375    public final long getConnectTimeMillis() {
1376        return mConnectTimeMillis;
1377    }
1378
1379    /**
1380     * @return The status hints for this connection.
1381     */
1382    public final StatusHints getStatusHints() {
1383        return mStatusHints;
1384    }
1385
1386    /**
1387     * Returns the extras associated with this connection.
1388     *
1389     * @return The extras associated with this connection.
1390     */
1391    public final Bundle getExtras() {
1392        Bundle extras = null;
1393        synchronized (mExtrasLock) {
1394            if (mExtras != null) {
1395                extras = new Bundle(mExtras);
1396            }
1397        }
1398        return extras;
1399    }
1400
1401    /**
1402     * Assign a listener to be notified of state changes.
1403     *
1404     * @param l A listener.
1405     * @return This Connection.
1406     *
1407     * @hide
1408     */
1409    public final Connection addConnectionListener(Listener l) {
1410        mListeners.add(l);
1411        return this;
1412    }
1413
1414    /**
1415     * Remove a previously assigned listener that was being notified of state changes.
1416     *
1417     * @param l A Listener.
1418     * @return This Connection.
1419     *
1420     * @hide
1421     */
1422    public final Connection removeConnectionListener(Listener l) {
1423        if (l != null) {
1424            mListeners.remove(l);
1425        }
1426        return this;
1427    }
1428
1429    /**
1430     * @return The {@link DisconnectCause} for this connection.
1431     */
1432    public final DisconnectCause getDisconnectCause() {
1433        return mDisconnectCause;
1434    }
1435
1436    /**
1437     * Sets the telecom call ID associated with this Connection.  The Telecom Call ID should be used
1438     * ONLY for debugging purposes.
1439     *
1440     * @param callId The telecom call ID.
1441     * @hide
1442     */
1443    public void setTelecomCallId(String callId) {
1444        mTelecomCallId = callId;
1445    }
1446
1447    /**
1448     * Inform this Connection that the state of its audio output has been changed externally.
1449     *
1450     * @param state The new audio state.
1451     * @hide
1452     */
1453    final void setCallAudioState(CallAudioState state) {
1454        checkImmutable();
1455        Log.d(this, "setAudioState %s", state);
1456        mCallAudioState = state;
1457        onAudioStateChanged(getAudioState());
1458        onCallAudioStateChanged(state);
1459    }
1460
1461    /**
1462     * @param state An integer value of a {@code STATE_*} constant.
1463     * @return A string representation of the value.
1464     */
1465    public static String stateToString(int state) {
1466        switch (state) {
1467            case STATE_INITIALIZING:
1468                return "INITIALIZING";
1469            case STATE_NEW:
1470                return "NEW";
1471            case STATE_RINGING:
1472                return "RINGING";
1473            case STATE_DIALING:
1474                return "DIALING";
1475            case STATE_ACTIVE:
1476                return "ACTIVE";
1477            case STATE_HOLDING:
1478                return "HOLDING";
1479            case STATE_DISCONNECTED:
1480                return "DISCONNECTED";
1481            default:
1482                Log.wtf(Connection.class, "Unknown state %d", state);
1483                return "UNKNOWN";
1484        }
1485    }
1486
1487    /**
1488     * Returns the connection's capabilities, as a bit mask of the {@code CAPABILITY_*} constants.
1489     */
1490    public final int getConnectionCapabilities() {
1491        return mConnectionCapabilities;
1492    }
1493
1494    /**
1495     * Returns the connection's properties, as a bit mask of the {@code PROPERTY_*} constants.
1496     * @hide
1497     */
1498    public final int getConnectionProperties() {
1499        return mConnectionProperties;
1500    }
1501
1502    /**
1503     * Sets the value of the {@link #getAddress()} property.
1504     *
1505     * @param address The new address.
1506     * @param presentation The presentation requirements for the address.
1507     *        See {@link TelecomManager} for valid values.
1508     */
1509    public final void setAddress(Uri address, int presentation) {
1510        checkImmutable();
1511        Log.d(this, "setAddress %s", address);
1512        mAddress = address;
1513        mAddressPresentation = presentation;
1514        for (Listener l : mListeners) {
1515            l.onAddressChanged(this, address, presentation);
1516        }
1517    }
1518
1519    /**
1520     * Sets the caller display name (CNAP).
1521     *
1522     * @param callerDisplayName The new display name.
1523     * @param presentation The presentation requirements for the handle.
1524     *        See {@link TelecomManager} for valid values.
1525     */
1526    public final void setCallerDisplayName(String callerDisplayName, int presentation) {
1527        checkImmutable();
1528        Log.d(this, "setCallerDisplayName %s", callerDisplayName);
1529        mCallerDisplayName = callerDisplayName;
1530        mCallerDisplayNamePresentation = presentation;
1531        for (Listener l : mListeners) {
1532            l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
1533        }
1534    }
1535
1536    /**
1537     * Set the video state for the connection.
1538     * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
1539     * {@link VideoProfile#STATE_BIDIRECTIONAL},
1540     * {@link VideoProfile#STATE_TX_ENABLED},
1541     * {@link VideoProfile#STATE_RX_ENABLED}.
1542     *
1543     * @param videoState The new video state.
1544     */
1545    public final void setVideoState(int videoState) {
1546        checkImmutable();
1547        Log.d(this, "setVideoState %d", videoState);
1548        mVideoState = videoState;
1549        for (Listener l : mListeners) {
1550            l.onVideoStateChanged(this, mVideoState);
1551        }
1552    }
1553
1554    /**
1555     * Sets state to active (e.g., an ongoing connection where two or more parties can actively
1556     * communicate).
1557     */
1558    public final void setActive() {
1559        checkImmutable();
1560        setRingbackRequested(false);
1561        setState(STATE_ACTIVE);
1562    }
1563
1564    /**
1565     * Sets state to ringing (e.g., an inbound ringing connection).
1566     */
1567    public final void setRinging() {
1568        checkImmutable();
1569        setState(STATE_RINGING);
1570    }
1571
1572    /**
1573     * Sets state to initializing (this Connection is not yet ready to be used).
1574     */
1575    public final void setInitializing() {
1576        checkImmutable();
1577        setState(STATE_INITIALIZING);
1578    }
1579
1580    /**
1581     * Sets state to initialized (the Connection has been set up and is now ready to be used).
1582     */
1583    public final void setInitialized() {
1584        checkImmutable();
1585        setState(STATE_NEW);
1586    }
1587
1588    /**
1589     * Sets state to dialing (e.g., dialing an outbound connection).
1590     */
1591    public final void setDialing() {
1592        checkImmutable();
1593        setState(STATE_DIALING);
1594    }
1595
1596    /**
1597     * Sets state to be on hold.
1598     */
1599    public final void setOnHold() {
1600        checkImmutable();
1601        setState(STATE_HOLDING);
1602    }
1603
1604    /**
1605     * Sets the video connection provider.
1606     * @param videoProvider The video provider.
1607     */
1608    public final void setVideoProvider(VideoProvider videoProvider) {
1609        checkImmutable();
1610        mVideoProvider = videoProvider;
1611        for (Listener l : mListeners) {
1612            l.onVideoProviderChanged(this, videoProvider);
1613        }
1614    }
1615
1616    public final VideoProvider getVideoProvider() {
1617        return mVideoProvider;
1618    }
1619
1620    /**
1621     * Sets state to disconnected.
1622     *
1623     * @param disconnectCause The reason for the disconnection, as specified by
1624     *         {@link DisconnectCause}.
1625     */
1626    public final void setDisconnected(DisconnectCause disconnectCause) {
1627        checkImmutable();
1628        mDisconnectCause = disconnectCause;
1629        setState(STATE_DISCONNECTED);
1630        Log.d(this, "Disconnected with cause %s", disconnectCause);
1631        for (Listener l : mListeners) {
1632            l.onDisconnected(this, disconnectCause);
1633        }
1634    }
1635
1636    /**
1637     * Informs listeners that this {@code Connection} is in a post-dial wait state. This is done
1638     * when (a) the {@code Connection} is issuing a DTMF sequence; (b) it has encountered a "wait"
1639     * character; and (c) it wishes to inform the In-Call app that it is waiting for the end-user
1640     * to send an {@link #onPostDialContinue(boolean)} signal.
1641     *
1642     * @param remaining The DTMF character sequence remaining to be emitted once the
1643     *         {@link #onPostDialContinue(boolean)} is received, including any "wait" characters
1644     *         that remaining sequence may contain.
1645     */
1646    public final void setPostDialWait(String remaining) {
1647        checkImmutable();
1648        for (Listener l : mListeners) {
1649            l.onPostDialWait(this, remaining);
1650        }
1651    }
1652
1653    /**
1654     * Informs listeners that this {@code Connection} has processed a character in the post-dial
1655     * started state. This is done when (a) the {@code Connection} is issuing a DTMF sequence;
1656     * and (b) it wishes to signal Telecom to play the corresponding DTMF tone locally.
1657     *
1658     * @param nextChar The DTMF character that was just processed by the {@code Connection}.
1659     */
1660    public final void setNextPostDialChar(char nextChar) {
1661        checkImmutable();
1662        for (Listener l : mListeners) {
1663            l.onPostDialChar(this, nextChar);
1664        }
1665    }
1666
1667    /**
1668     * Requests that the framework play a ringback tone. This is to be invoked by implementations
1669     * that do not play a ringback tone themselves in the connection's audio stream.
1670     *
1671     * @param ringback Whether the ringback tone is to be played.
1672     */
1673    public final void setRingbackRequested(boolean ringback) {
1674        checkImmutable();
1675        if (mRingbackRequested != ringback) {
1676            mRingbackRequested = ringback;
1677            for (Listener l : mListeners) {
1678                l.onRingbackRequested(this, ringback);
1679            }
1680        }
1681    }
1682
1683    /**
1684     * Sets the connection's capabilities as a bit mask of the {@code CAPABILITY_*} constants.
1685     *
1686     * @param connectionCapabilities The new connection capabilities.
1687     */
1688    public final void setConnectionCapabilities(int connectionCapabilities) {
1689        checkImmutable();
1690        if (mConnectionCapabilities != connectionCapabilities) {
1691            mConnectionCapabilities = connectionCapabilities;
1692            for (Listener l : mListeners) {
1693                l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
1694            }
1695        }
1696    }
1697
1698    /**
1699     * Sets the connection's properties as a bit mask of the {@code PROPERTY_*} constants.
1700     *
1701     * @param connectionProperties The new connection properties.
1702     * @hide
1703     */
1704    public final void setConnectionProperties(int connectionProperties) {
1705        checkImmutable();
1706        if (mConnectionProperties != connectionProperties) {
1707            mConnectionProperties = connectionProperties;
1708            for (Listener l : mListeners) {
1709                l.onConnectionPropertiesChanged(this, mConnectionProperties);
1710            }
1711        }
1712    }
1713
1714    /**
1715     * Tears down the Connection object.
1716     */
1717    public final void destroy() {
1718        for (Listener l : mListeners) {
1719            l.onDestroyed(this);
1720        }
1721    }
1722
1723    /**
1724     * Requests that the framework use VOIP audio mode for this connection.
1725     *
1726     * @param isVoip True if the audio mode is VOIP.
1727     */
1728    public final void setAudioModeIsVoip(boolean isVoip) {
1729        checkImmutable();
1730        mAudioModeIsVoip = isVoip;
1731        for (Listener l : mListeners) {
1732            l.onAudioModeIsVoipChanged(this, isVoip);
1733        }
1734    }
1735
1736    /**
1737     * Sets the time at which a call became active on this Connection. This is set only
1738     * when a conference call becomes active on this connection.
1739     *
1740     * @param connectionTimeMillis The connection time, in milliseconds.
1741     *
1742     * @hide
1743     */
1744    public final void setConnectTimeMillis(long connectTimeMillis) {
1745        mConnectTimeMillis = connectTimeMillis;
1746    }
1747
1748    /**
1749     * Sets the label and icon status to display in the in-call UI.
1750     *
1751     * @param statusHints The status label and icon to set.
1752     */
1753    public final void setStatusHints(StatusHints statusHints) {
1754        checkImmutable();
1755        mStatusHints = statusHints;
1756        for (Listener l : mListeners) {
1757            l.onStatusHintsChanged(this, statusHints);
1758        }
1759    }
1760
1761    /**
1762     * Sets the connections with which this connection can be conferenced.
1763     *
1764     * @param conferenceableConnections The set of connections this connection can conference with.
1765     */
1766    public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
1767        checkImmutable();
1768        clearConferenceableList();
1769        for (Connection c : conferenceableConnections) {
1770            // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
1771            // small amount of items here.
1772            if (!mConferenceables.contains(c)) {
1773                c.addConnectionListener(mConnectionDeathListener);
1774                mConferenceables.add(c);
1775            }
1776        }
1777        fireOnConferenceableConnectionsChanged();
1778    }
1779
1780    /**
1781     * Similar to {@link #setConferenceableConnections(java.util.List)}, sets a list of connections
1782     * or conferences with which this connection can be conferenced.
1783     *
1784     * @param conferenceables The conferenceables.
1785     */
1786    public final void setConferenceables(List<Conferenceable> conferenceables) {
1787        clearConferenceableList();
1788        for (Conferenceable c : conferenceables) {
1789            // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
1790            // small amount of items here.
1791            if (!mConferenceables.contains(c)) {
1792                if (c instanceof Connection) {
1793                    Connection connection = (Connection) c;
1794                    connection.addConnectionListener(mConnectionDeathListener);
1795                } else if (c instanceof Conference) {
1796                    Conference conference = (Conference) c;
1797                    conference.addListener(mConferenceDeathListener);
1798                }
1799                mConferenceables.add(c);
1800            }
1801        }
1802        fireOnConferenceableConnectionsChanged();
1803    }
1804
1805    /**
1806     * Returns the connections or conferences with which this connection can be conferenced.
1807     */
1808    public final List<Conferenceable> getConferenceables() {
1809        return mUnmodifiableConferenceables;
1810    }
1811
1812    /**
1813     * @hide
1814     */
1815    public final void setConnectionService(ConnectionService connectionService) {
1816        checkImmutable();
1817        if (mConnectionService != null) {
1818            Log.e(this, new Exception(), "Trying to set ConnectionService on a connection " +
1819                    "which is already associated with another ConnectionService.");
1820        } else {
1821            mConnectionService = connectionService;
1822        }
1823    }
1824
1825    /**
1826     * @hide
1827     */
1828    public final void unsetConnectionService(ConnectionService connectionService) {
1829        if (mConnectionService != connectionService) {
1830            Log.e(this, new Exception(), "Trying to remove ConnectionService from a Connection " +
1831                    "that does not belong to the ConnectionService.");
1832        } else {
1833            mConnectionService = null;
1834        }
1835    }
1836
1837    /**
1838     * @hide
1839     */
1840    public final ConnectionService getConnectionService() {
1841        return mConnectionService;
1842    }
1843
1844    /**
1845     * Sets the conference that this connection is a part of. This will fail if the connection is
1846     * already part of a conference. {@link #resetConference} to un-set the conference first.
1847     *
1848     * @param conference The conference.
1849     * @return {@code true} if the conference was successfully set.
1850     * @hide
1851     */
1852    public final boolean setConference(Conference conference) {
1853        checkImmutable();
1854        // We check to see if it is already part of another conference.
1855        if (mConference == null) {
1856            mConference = conference;
1857            if (mConnectionService != null && mConnectionService.containsConference(conference)) {
1858                fireConferenceChanged();
1859            }
1860            return true;
1861        }
1862        return false;
1863    }
1864
1865    /**
1866     * Resets the conference that this connection is a part of.
1867     * @hide
1868     */
1869    public final void resetConference() {
1870        if (mConference != null) {
1871            Log.d(this, "Conference reset");
1872            mConference = null;
1873            fireConferenceChanged();
1874        }
1875    }
1876
1877    /**
1878     * Set some extras that can be associated with this {@code Connection}.
1879     * <p>
1880     * New or existing keys are replaced in the {@code Connection} extras.  Keys which are no longer
1881     * in the new extras, but were present the last time {@code setExtras} was called are removed.
1882     * <p>
1883     * No assumptions should be made as to how an In-Call UI or service will handle these extras.
1884     * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
1885     *
1886     * @param extras The extras associated with this {@code Connection}.
1887     */
1888    public final void setExtras(@Nullable Bundle extras) {
1889        checkImmutable();
1890
1891        // Add/replace any new or changed extras values.
1892        putExtras(extras);
1893
1894        // If we have used "setExtras" in the past, compare the key set from the last invocation to
1895        // the current one and remove any keys that went away.
1896        if (mPreviousExtraKeys != null) {
1897            List<String> toRemove = new ArrayList<String>();
1898            for (String oldKey : mPreviousExtraKeys) {
1899                if (extras == null || !extras.containsKey(oldKey)) {
1900                    toRemove.add(oldKey);
1901                }
1902            }
1903            if (!toRemove.isEmpty()) {
1904                removeExtras(toRemove);
1905            }
1906        }
1907
1908        // Track the keys the last time set called setExtras.  This way, the next time setExtras is
1909        // called we can see if the caller has removed any extras values.
1910        if (mPreviousExtraKeys == null) {
1911            mPreviousExtraKeys = new ArraySet<String>();
1912        }
1913        mPreviousExtraKeys.clear();
1914        if (extras != null) {
1915            mPreviousExtraKeys.addAll(extras.keySet());
1916        }
1917    }
1918
1919    /**
1920     * Adds some extras to this {@code Connection}.  Existing keys are replaced and new ones are
1921     * added.
1922     * <p>
1923     * No assumptions should be made as to how an In-Call UI or service will handle these extras.
1924     * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
1925     *
1926     * @param extras The extras to add.
1927     * @hide
1928     */
1929    public final void putExtras(@NonNull Bundle extras) {
1930        checkImmutable();
1931        if (extras == null) {
1932            return;
1933        }
1934        // Creating a duplicate bundle so we don't have to synchronize on mExtrasLock while calling
1935        // the listeners.
1936        Bundle listenerExtras;
1937        synchronized (mExtrasLock) {
1938            if (mExtras == null) {
1939                mExtras = new Bundle();
1940            }
1941            mExtras.putAll(extras);
1942            listenerExtras = new Bundle(mExtras);
1943        }
1944        for (Listener l : mListeners) {
1945            // Create a new clone of the extras for each listener so that they don't clobber
1946            // each other
1947            l.onExtrasChanged(this, new Bundle(listenerExtras));
1948        }
1949    }
1950
1951    /**
1952     * Adds a boolean extra to this {@code Connection}.
1953     *
1954     * @param key The extra key.
1955     * @param value The value.
1956     * @hide
1957     */
1958    public final void putExtra(String key, boolean value) {
1959        Bundle newExtras = new Bundle();
1960        newExtras.putBoolean(key, value);
1961        putExtras(newExtras);
1962    }
1963
1964    /**
1965     * Adds an integer extra to this {@code Connection}.
1966     *
1967     * @param key The extra key.
1968     * @param value The value.
1969     * @hide
1970     */
1971    public final void putExtra(String key, int value) {
1972        Bundle newExtras = new Bundle();
1973        newExtras.putInt(key, value);
1974        putExtras(newExtras);
1975    }
1976
1977    /**
1978     * Adds a string extra to this {@code Connection}.
1979     *
1980     * @param key The extra key.
1981     * @param value The value.
1982     * @hide
1983     */
1984    public final void putExtra(String key, String value) {
1985        Bundle newExtras = new Bundle();
1986        newExtras.putString(key, value);
1987        putExtras(newExtras);
1988    }
1989
1990    /**
1991     * Removes an extra from this {@code Connection}.
1992     *
1993     * @param keys The key of the extra key to remove.
1994     * @hide
1995     */
1996    public final void removeExtras(List<String> keys) {
1997        synchronized (mExtrasLock) {
1998            if (mExtras != null) {
1999                for (String key : keys) {
2000                    mExtras.remove(key);
2001                }
2002            }
2003        }
2004        List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
2005        for (Listener l : mListeners) {
2006            l.onExtrasRemoved(this, unmodifiableKeys);
2007        }
2008    }
2009
2010    /**
2011     * Notifies this Connection that the {@link #getAudioState()} property has a new value.
2012     *
2013     * @param state The new connection audio state.
2014     * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
2015     * @hide
2016     */
2017    @SystemApi
2018    @Deprecated
2019    public void onAudioStateChanged(AudioState state) {}
2020
2021    /**
2022     * Notifies this Connection that the {@link #getCallAudioState()} property has a new value.
2023     *
2024     * @param state The new connection audio state.
2025     */
2026    public void onCallAudioStateChanged(CallAudioState state) {}
2027
2028    /**
2029     * Notifies this Connection of an internal state change. This method is called after the
2030     * state is changed.
2031     *
2032     * @param state The new state, one of the {@code STATE_*} constants.
2033     */
2034    public void onStateChanged(int state) {}
2035
2036    /**
2037     * Notifies this Connection of a request to play a DTMF tone.
2038     *
2039     * @param c A DTMF character.
2040     */
2041    public void onPlayDtmfTone(char c) {}
2042
2043    /**
2044     * Notifies this Connection of a request to stop any currently playing DTMF tones.
2045     */
2046    public void onStopDtmfTone() {}
2047
2048    /**
2049     * Notifies this Connection of a request to disconnect.
2050     */
2051    public void onDisconnect() {}
2052
2053    /**
2054     * Notifies this Connection of a request to disconnect a participant of the conference managed
2055     * by the connection.
2056     *
2057     * @param endpoint the {@link Uri} of the participant to disconnect.
2058     * @hide
2059     */
2060    public void onDisconnectConferenceParticipant(Uri endpoint) {}
2061
2062    /**
2063     * Notifies this Connection of a request to separate from its parent conference.
2064     */
2065    public void onSeparate() {}
2066
2067    /**
2068     * Notifies this Connection of a request to abort.
2069     */
2070    public void onAbort() {}
2071
2072    /**
2073     * Notifies this Connection of a request to hold.
2074     */
2075    public void onHold() {}
2076
2077    /**
2078     * Notifies this Connection of a request to exit a hold state.
2079     */
2080    public void onUnhold() {}
2081
2082    /**
2083     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2084     * a request to accept.
2085     *
2086     * @param videoState The video state in which to answer the connection.
2087     */
2088    public void onAnswer(int videoState) {}
2089
2090    /**
2091     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2092     * a request to accept.
2093     */
2094    public void onAnswer() {
2095        onAnswer(VideoProfile.STATE_AUDIO_ONLY);
2096    }
2097
2098    /**
2099     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2100     * a request to reject.
2101     */
2102    public void onReject() {}
2103
2104    /**
2105     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2106     * a request to reject with a message.
2107     */
2108    public void onReject(String replyMessage) {}
2109
2110    /**
2111     * Notifies the Connection of a request to silence the ringer.
2112     *
2113     * @hide
2114     */
2115    public void onSilence() {}
2116
2117    /**
2118     * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
2119     */
2120    public void onPostDialContinue(boolean proceed) {}
2121
2122    /**
2123     * Notifies this Connection of a request to pull an external call to the local device.
2124     * <p>
2125     * The {@link InCallService} issues a request to pull an external call to the local device via
2126     * {@link Call#pullExternalCall()}.
2127     * <p>
2128     * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL}
2129     * capability and {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property bits must be set.
2130     * <p>
2131     * For more information on external calls, see {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
2132     * @hide
2133     */
2134    public void onPullExternalCall() {}
2135
2136    /**
2137     * Notifies this Connection of a {@link Call} event initiated from an {@link InCallService}.
2138     * <p>
2139     * The {@link InCallService} issues a Call event via {@link Call#sendCallEvent(String, Bundle)}.
2140     * <p>
2141     * See also {@link Call#sendCallEvent(String, Bundle)}.
2142     *
2143     * @param event The call event.
2144     * @param extras Extras associated with the call event.
2145     * @hide
2146     */
2147    public void onCallEvent(String event, Bundle extras) {}
2148
2149    /**
2150     * Notifies this {@link Connection} of a change to the extras made outside the
2151     * {@link ConnectionService}.
2152     * <p>
2153     * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
2154     * the {@link android.telecom.Call#putExtras(Bundle)} and
2155     * {@link Call#removeExtras(List)}.
2156     *
2157     * @param extras The new extras bundle.
2158     * @hide
2159     */
2160    public void onExtrasChanged(Bundle extras) {}
2161
2162    static String toLogSafePhoneNumber(String number) {
2163        // For unknown number, log empty string.
2164        if (number == null) {
2165            return "";
2166        }
2167
2168        if (PII_DEBUG) {
2169            // When PII_DEBUG is true we emit PII.
2170            return number;
2171        }
2172
2173        // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
2174        // sanitized phone numbers.
2175        StringBuilder builder = new StringBuilder();
2176        for (int i = 0; i < number.length(); i++) {
2177            char c = number.charAt(i);
2178            if (c == '-' || c == '@' || c == '.') {
2179                builder.append(c);
2180            } else {
2181                builder.append('x');
2182            }
2183        }
2184        return builder.toString();
2185    }
2186
2187    private void setState(int state) {
2188        checkImmutable();
2189        if (mState == STATE_DISCONNECTED && mState != state) {
2190            Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
2191            return;
2192        }
2193        if (mState != state) {
2194            Log.d(this, "setState: %s", stateToString(state));
2195            mState = state;
2196            onStateChanged(state);
2197            for (Listener l : mListeners) {
2198                l.onStateChanged(this, state);
2199            }
2200        }
2201    }
2202
2203    private static class FailureSignalingConnection extends Connection {
2204        private boolean mImmutable = false;
2205        public FailureSignalingConnection(DisconnectCause disconnectCause) {
2206            setDisconnected(disconnectCause);
2207            mImmutable = true;
2208        }
2209
2210        public void checkImmutable() {
2211            if (mImmutable) {
2212                throw new UnsupportedOperationException("Connection is immutable");
2213            }
2214        }
2215    }
2216
2217    /**
2218     * Return a {@code Connection} which represents a failed connection attempt. The returned
2219     * {@code Connection} will have a {@link android.telecom.DisconnectCause} and as specified,
2220     * and a {@link #getState()} of {@link #STATE_DISCONNECTED}.
2221     * <p>
2222     * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
2223     * so users of this method need not maintain a reference to its return value to destroy it.
2224     *
2225     * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
2226     * @return A {@code Connection} which indicates failure.
2227     */
2228    public static Connection createFailedConnection(DisconnectCause disconnectCause) {
2229        return new FailureSignalingConnection(disconnectCause);
2230    }
2231
2232    /**
2233     * Override to throw an {@link UnsupportedOperationException} if this {@code Connection} is
2234     * not intended to be mutated, e.g., if it is a marker for failure. Only for framework use;
2235     * this should never be un-@hide-den.
2236     *
2237     * @hide
2238     */
2239    public void checkImmutable() {}
2240
2241    /**
2242     * Return a {@code Connection} which represents a canceled connection attempt. The returned
2243     * {@code Connection} will have state {@link #STATE_DISCONNECTED}, and cannot be moved out of
2244     * that state. This connection should not be used for anything, and no other
2245     * {@code Connection}s should be attempted.
2246     * <p>
2247     * so users of this method need not maintain a reference to its return value to destroy it.
2248     *
2249     * @return A {@code Connection} which indicates that the underlying connection should
2250     * be canceled.
2251     */
2252    public static Connection createCanceledConnection() {
2253        return new FailureSignalingConnection(new DisconnectCause(DisconnectCause.CANCELED));
2254    }
2255
2256    private final void fireOnConferenceableConnectionsChanged() {
2257        for (Listener l : mListeners) {
2258            l.onConferenceablesChanged(this, getConferenceables());
2259        }
2260    }
2261
2262    private final void fireConferenceChanged() {
2263        for (Listener l : mListeners) {
2264            l.onConferenceChanged(this, mConference);
2265        }
2266    }
2267
2268    private final void clearConferenceableList() {
2269        for (Conferenceable c : mConferenceables) {
2270            if (c instanceof Connection) {
2271                Connection connection = (Connection) c;
2272                connection.removeConnectionListener(mConnectionDeathListener);
2273            } else if (c instanceof Conference) {
2274                Conference conference = (Conference) c;
2275                conference.removeListener(mConferenceDeathListener);
2276            }
2277        }
2278        mConferenceables.clear();
2279    }
2280
2281    /**
2282     * Handles a change to extras received from Telecom.
2283     *
2284     * @param extras The new extras.
2285     * @hide
2286     */
2287    final void handleExtrasChanged(Bundle extras) {
2288        Bundle b = null;
2289        synchronized (mExtrasLock) {
2290            mExtras = extras;
2291            if (mExtras != null) {
2292                b = new Bundle(mExtras);
2293            }
2294        }
2295        onExtrasChanged(b);
2296    }
2297
2298    /**
2299     * Notifies listeners that the merge request failed.
2300     *
2301     * @hide
2302     */
2303    protected final void notifyConferenceMergeFailed() {
2304        for (Listener l : mListeners) {
2305            l.onConferenceMergeFailed(this);
2306        }
2307    }
2308
2309    /**
2310     * Notifies listeners of a change to conference participant(s).
2311     *
2312     * @param conferenceParticipants The participants.
2313     * @hide
2314     */
2315    protected final void updateConferenceParticipants(
2316            List<ConferenceParticipant> conferenceParticipants) {
2317        for (Listener l : mListeners) {
2318            l.onConferenceParticipantsChanged(this, conferenceParticipants);
2319        }
2320    }
2321
2322    /**
2323     * Notifies listeners that a conference call has been started.
2324     * @hide
2325     */
2326    protected void notifyConferenceStarted() {
2327        for (Listener l : mListeners) {
2328            l.onConferenceStarted();
2329        }
2330    }
2331
2332    /**
2333     * Sends an event associated with this {@code Connection}, with associated event extras.
2334     *
2335     * Events are exposed to {@link InCallService} implementations via the
2336     * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)} API.
2337     *
2338     * No assumptions should be made as to how an In-Call UI or service will handle these events.
2339     * Events should be fully qualified (e.g., com.example.event.MY_EVENT) to avoid conflicts.
2340     *
2341     * @param event The connection event.
2342     * @param extras Bundle containing extra information associated with the event.
2343     * @hide
2344     */
2345    public void sendConnectionEvent(String event, Bundle extras) {
2346        for (Listener l : mListeners) {
2347            l.onConnectionEvent(this, event, extras);
2348        }
2349    }
2350}
2351