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