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