Connection.java revision 12ca74e2a0b26d1805b3caac9488961078b5409b
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.telecom.IVideoCallback;
20import com.android.internal.telecom.IVideoProvider;
21
22import android.annotation.SystemApi;
23import android.net.Uri;
24import android.os.Handler;
25import android.os.IBinder;
26import android.os.Message;
27import android.os.RemoteException;
28import android.view.Surface;
29
30import java.util.ArrayList;
31import java.util.Collections;
32import java.util.List;
33import java.util.Set;
34import java.util.concurrent.ConcurrentHashMap;
35
36/**
37 * Represents a connection to a remote endpoint that carries voice traffic.
38 * <p>
39 * Implementations create a custom subclass of {@code Connection} and return it to the framework
40 * as the return value of
41 * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
42 * or
43 * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
44 * Implementations are then responsible for updating the state of the {@code Connection}, and
45 * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
46 * longer used and associated resources may be recovered.
47 * @hide
48 */
49@SystemApi
50public abstract class Connection implements IConferenceable {
51
52    public static final int STATE_INITIALIZING = 0;
53
54    public static final int STATE_NEW = 1;
55
56    public static final int STATE_RINGING = 2;
57
58    public static final int STATE_DIALING = 3;
59
60    public static final int STATE_ACTIVE = 4;
61
62    public static final int STATE_HOLDING = 5;
63
64    public static final int STATE_DISCONNECTED = 6;
65
66    /** Connection can currently be put on hold or unheld. */
67    public static final int CAPABILITY_HOLD = 0x00000001;
68
69    /** Connection supports the hold feature. */
70    public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
71
72    /**
73     * Connections within a conference can be merged. A {@link ConnectionService} has the option to
74     * add a {@link Conference} before the child {@link Connection}s are merged. This is how
75     * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
76     * capability allows a merge button to be shown while the conference is in the foreground
77     * of the in-call UI.
78     * <p>
79     * This is only intended for use by a {@link Conference}.
80     */
81    public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
82
83    /**
84     * Connections within a conference can be swapped between foreground and background.
85     * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
86     * <p>
87     * This is only intended for use by a {@link Conference}.
88     */
89    public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
90
91    /**
92     * @hide
93     */
94    public static final int CAPABILITY_UNUSED = 0x00000010;
95
96    /** Connection supports responding via text option. */
97    public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
98
99    /** Connection can be muted. */
100    public static final int CAPABILITY_MUTE = 0x00000040;
101
102    /**
103     * Connection supports conference management. This capability only applies to
104     * {@link Conference}s which can have {@link Connection}s as children.
105     */
106    public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
107
108    /**
109     * Local device supports video telephony.
110     * @hide
111     */
112    public static final int CAPABILITY_SUPPORTS_VT_LOCAL = 0x00000100;
113
114    /**
115     * Remote device supports video telephony.
116     * @hide
117     */
118    public static final int CAPABILITY_SUPPORTS_VT_REMOTE = 0x00000200;
119
120    /**
121     * Connection is using high definition audio.
122     * @hide
123     */
124    public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00000400;
125
126    /**
127     * Connection is using voice over WIFI.
128     * @hide
129     */
130    public static final int CAPABILITY_VoWIFI = 0x00000800;
131
132    /**
133     * Connection is able to be separated from its parent {@code Conference}, if any.
134     */
135    public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
136
137    /**
138     * Connection is able to be individually disconnected when in a {@code Conference}.
139     */
140    public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
141
142    /**
143     * Whether the call is a generic conference, where we do not know the precise state of
144     * participants in the conference (eg. on CDMA).
145     *
146     * @hide
147     */
148    public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000;
149
150    // Flag controlling whether PII is emitted into the logs
151    private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
152
153    /**
154     * Whether the given capabilities support the specified capability.
155     *
156     * @param capabilities A capability bit field.
157     * @param capability The capability to check capabilities for.
158     * @return Whether the specified capability is supported.
159     * @hide
160     */
161    public static boolean can(int capabilities, int capability) {
162        return (capabilities & capability) != 0;
163    }
164
165    /**
166     * Whether the capabilities of this {@code Connection} supports the specified capability.
167     *
168     * @param capability The capability to check capabilities for.
169     * @return Whether the specified capability is supported.
170     * @hide
171     */
172    public boolean can(int capability) {
173        return can(mConnectionCapabilities, capability);
174    }
175
176    /**
177     * Removes the specified capability from the set of capabilities of this {@code Connection}.
178     *
179     * @param capability The capability to remove from the set.
180     * @hide
181     */
182    public void removeCapability(int capability) {
183        mConnectionCapabilities &= ~capability;
184    }
185
186    /**
187     * Adds the specified capability to the set of capabilities of this {@code Connection}.
188     *
189     * @param capability The capability to add to the set.
190     * @hide
191     */
192    public void addCapability(int capability) {
193        mConnectionCapabilities |= capability;
194    }
195
196
197    public static String capabilitiesToString(int capabilities) {
198        StringBuilder builder = new StringBuilder();
199        builder.append("[Capabilities:");
200        if (can(capabilities, CAPABILITY_HOLD)) {
201            builder.append(" CAPABILITY_HOLD");
202        }
203        if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
204            builder.append(" CAPABILITY_SUPPORT_HOLD");
205        }
206        if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
207            builder.append(" CAPABILITY_MERGE_CONFERENCE");
208        }
209        if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
210            builder.append(" CAPABILITY_SWAP_CONFERENCE");
211        }
212        if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
213            builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
214        }
215        if (can(capabilities, CAPABILITY_MUTE)) {
216            builder.append(" CAPABILITY_MUTE");
217        }
218        if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
219            builder.append(" CAPABILITY_MANAGE_CONFERENCE");
220        }
221        if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL)) {
222            builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL");
223        }
224        if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE)) {
225            builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE");
226        }
227        if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
228            builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
229        }
230        if (can(capabilities, CAPABILITY_VoWIFI)) {
231            builder.append(" CAPABILITY_VoWIFI");
232        }
233        if (can(capabilities, CAPABILITY_GENERIC_CONFERENCE)) {
234            builder.append(" CAPABILITY_GENERIC_CONFERENCE");
235        }
236        builder.append("]");
237        return builder.toString();
238    }
239
240    /** @hide */
241    public abstract static class Listener {
242        public void onStateChanged(Connection c, int state) {}
243        public void onAddressChanged(Connection c, Uri newAddress, int presentation) {}
244        public void onCallerDisplayNameChanged(
245                Connection c, String callerDisplayName, int presentation) {}
246        public void onVideoStateChanged(Connection c, int videoState) {}
247        public void onDisconnected(Connection c, DisconnectCause disconnectCause) {}
248        public void onPostDialWait(Connection c, String remaining) {}
249        public void onPostDialChar(Connection c, char nextChar) {}
250        public void onRingbackRequested(Connection c, boolean ringback) {}
251        public void onDestroyed(Connection c) {}
252        public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {}
253        public void onVideoProviderChanged(
254                Connection c, VideoProvider videoProvider) {}
255        public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
256        public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
257        public void onConferenceablesChanged(
258                Connection c, List<IConferenceable> conferenceables) {}
259        public void onConferenceChanged(Connection c, Conference conference) {}
260        /** @hide */
261        public void onConferenceParticipantsChanged(Connection c,
262                List<ConferenceParticipant> participants) {}
263    }
264
265    /** @hide */
266    public static abstract class VideoProvider {
267
268        /**
269         * Video is not being received (no protocol pause was issued).
270         */
271        public static final int SESSION_EVENT_RX_PAUSE = 1;
272
273        /**
274         * Video reception has resumed after a SESSION_EVENT_RX_PAUSE.
275         */
276        public static final int SESSION_EVENT_RX_RESUME = 2;
277
278        /**
279         * Video transmission has begun. This occurs after a negotiated start of video transmission
280         * when the underlying protocol has actually begun transmitting video to the remote party.
281         */
282        public static final int SESSION_EVENT_TX_START = 3;
283
284        /**
285         * Video transmission has stopped. This occurs after a negotiated stop of video transmission
286         * when the underlying protocol has actually stopped transmitting video to the remote party.
287         */
288        public static final int SESSION_EVENT_TX_STOP = 4;
289
290        /**
291         * A camera failure has occurred for the selected camera.  The In-Call UI can use this as a
292         * cue to inform the user the camera is not available.
293         */
294        public static final int SESSION_EVENT_CAMERA_FAILURE = 5;
295
296        /**
297         * Issued after {@code SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready for
298         * operation.  The In-Call UI can use this as a cue to inform the user that the camera has
299         * become available again.
300         */
301        public static final int SESSION_EVENT_CAMERA_READY = 6;
302
303        /**
304         * Session modify request was successful.
305         */
306        public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
307
308        /**
309         * Session modify request failed.
310         */
311        public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
312
313        /**
314         * Session modify request ignored due to invalid parameters.
315         */
316        public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
317
318        private static final int MSG_SET_VIDEO_CALLBACK = 1;
319        private static final int MSG_SET_CAMERA = 2;
320        private static final int MSG_SET_PREVIEW_SURFACE = 3;
321        private static final int MSG_SET_DISPLAY_SURFACE = 4;
322        private static final int MSG_SET_DEVICE_ORIENTATION = 5;
323        private static final int MSG_SET_ZOOM = 6;
324        private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
325        private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
326        private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
327        private static final int MSG_REQUEST_CONNECTION_DATA_USAGE = 10;
328        private static final int MSG_SET_PAUSE_IMAGE = 11;
329
330        private final VideoProvider.VideoProviderHandler
331                mMessageHandler = new VideoProvider.VideoProviderHandler();
332        private final VideoProvider.VideoProviderBinder mBinder;
333        private IVideoCallback mVideoCallback;
334
335        /**
336         * Default handler used to consolidate binder method calls onto a single thread.
337         */
338        private final class VideoProviderHandler extends Handler {
339            @Override
340            public void handleMessage(Message msg) {
341                switch (msg.what) {
342                    case MSG_SET_VIDEO_CALLBACK:
343                        mVideoCallback = IVideoCallback.Stub.asInterface((IBinder) msg.obj);
344                        break;
345                    case MSG_SET_CAMERA:
346                        onSetCamera((String) msg.obj);
347                        break;
348                    case MSG_SET_PREVIEW_SURFACE:
349                        onSetPreviewSurface((Surface) msg.obj);
350                        break;
351                    case MSG_SET_DISPLAY_SURFACE:
352                        onSetDisplaySurface((Surface) msg.obj);
353                        break;
354                    case MSG_SET_DEVICE_ORIENTATION:
355                        onSetDeviceOrientation(msg.arg1);
356                        break;
357                    case MSG_SET_ZOOM:
358                        onSetZoom((Float) msg.obj);
359                        break;
360                    case MSG_SEND_SESSION_MODIFY_REQUEST:
361                        onSendSessionModifyRequest((VideoProfile) msg.obj);
362                        break;
363                    case MSG_SEND_SESSION_MODIFY_RESPONSE:
364                        onSendSessionModifyResponse((VideoProfile) msg.obj);
365                        break;
366                    case MSG_REQUEST_CAMERA_CAPABILITIES:
367                        onRequestCameraCapabilities();
368                        break;
369                    case MSG_REQUEST_CONNECTION_DATA_USAGE:
370                        onRequestConnectionDataUsage();
371                        break;
372                    case MSG_SET_PAUSE_IMAGE:
373                        onSetPauseImage((String) msg.obj);
374                        break;
375                    default:
376                        break;
377                }
378            }
379        }
380
381        /**
382         * IVideoProvider stub implementation.
383         */
384        private final class VideoProviderBinder extends IVideoProvider.Stub {
385            public void setVideoCallback(IBinder videoCallbackBinder) {
386                mMessageHandler.obtainMessage(
387                        MSG_SET_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
388            }
389
390            public void setCamera(String cameraId) {
391                mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
392            }
393
394            public void setPreviewSurface(Surface surface) {
395                mMessageHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
396            }
397
398            public void setDisplaySurface(Surface surface) {
399                mMessageHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
400            }
401
402            public void setDeviceOrientation(int rotation) {
403                mMessageHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation).sendToTarget();
404            }
405
406            public void setZoom(float value) {
407                mMessageHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
408            }
409
410            public void sendSessionModifyRequest(VideoProfile requestProfile) {
411                mMessageHandler.obtainMessage(
412                        MSG_SEND_SESSION_MODIFY_REQUEST, requestProfile).sendToTarget();
413            }
414
415            public void sendSessionModifyResponse(VideoProfile responseProfile) {
416                mMessageHandler.obtainMessage(
417                        MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
418            }
419
420            public void requestCameraCapabilities() {
421                mMessageHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
422            }
423
424            public void requestCallDataUsage() {
425                mMessageHandler.obtainMessage(MSG_REQUEST_CONNECTION_DATA_USAGE).sendToTarget();
426            }
427
428            public void setPauseImage(String uri) {
429                mMessageHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
430            }
431        }
432
433        public VideoProvider() {
434            mBinder = new VideoProvider.VideoProviderBinder();
435        }
436
437        /**
438         * Returns binder object which can be used across IPC methods.
439         * @hide
440         */
441        public final IVideoProvider getInterface() {
442            return mBinder;
443        }
444
445        /**
446         * Sets the camera to be used for video recording in a video connection.
447         *
448         * @param cameraId The id of the camera.
449         */
450        public abstract void onSetCamera(String cameraId);
451
452        /**
453         * Sets the surface to be used for displaying a preview of what the user's camera is
454         * currently capturing.  When video transmission is enabled, this is the video signal which
455         * is sent to the remote device.
456         *
457         * @param surface The surface.
458         */
459        public abstract void onSetPreviewSurface(Surface surface);
460
461        /**
462         * Sets the surface to be used for displaying the video received from the remote device.
463         *
464         * @param surface The surface.
465         */
466        public abstract void onSetDisplaySurface(Surface surface);
467
468        /**
469         * Sets the device orientation, in degrees.  Assumes that a standard portrait orientation of
470         * the device is 0 degrees.
471         *
472         * @param rotation The device orientation, in degrees.
473         */
474        public abstract void onSetDeviceOrientation(int rotation);
475
476        /**
477         * Sets camera zoom ratio.
478         *
479         * @param value The camera zoom ratio.
480         */
481        public abstract void onSetZoom(float value);
482
483        /**
484         * Issues a request to modify the properties of the current session.  The request is
485         * sent to the remote device where it it handled by the In-Call UI.
486         * Some examples of session modification requests: upgrade connection from audio to video,
487         * downgrade connection from video to audio, pause video.
488         *
489         * @param requestProfile The requested connection video properties.
490         */
491        public abstract void onSendSessionModifyRequest(VideoProfile requestProfile);
492
493        /**te
494         * Provides a response to a request to change the current connection session video
495         * properties.
496         * This is in response to a request the InCall UI has received via the InCall UI.
497         *
498         * @param responseProfile The response connection video properties.
499         */
500        public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
501
502        /**
503         * Issues a request to the video provider to retrieve the camera capabilities.
504         * Camera capabilities are reported back to the caller via the In-Call UI.
505         */
506        public abstract void onRequestCameraCapabilities();
507
508        /**
509         * Issues a request to the video telephony framework to retrieve the cumulative data usage
510         * for the current connection.  Data usage is reported back to the caller via the
511         * InCall UI.
512         */
513        public abstract void onRequestConnectionDataUsage();
514
515        /**
516         * Provides the video telephony framework with the URI of an image to be displayed to remote
517         * devices when the video signal is paused.
518         *
519         * @param uri URI of image to display.
520         */
521        public abstract void onSetPauseImage(String uri);
522
523        /**
524         * Invokes callback method defined in In-Call UI.
525         *
526         * @param videoProfile The requested video connection profile.
527         */
528        public void receiveSessionModifyRequest(VideoProfile videoProfile) {
529            if (mVideoCallback != null) {
530                try {
531                    mVideoCallback.receiveSessionModifyRequest(videoProfile);
532                } catch (RemoteException ignored) {
533                }
534            }
535        }
536
537        /**
538         * Invokes callback method defined in In-Call UI.
539         *
540         * @param status Status of the session modify request.  Valid values are
541         *               {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
542         *               {@link VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
543         *               {@link VideoProvider#SESSION_MODIFY_REQUEST_INVALID}
544         * @param requestedProfile The original request which was sent to the remote device.
545         * @param responseProfile The actual profile changes made by the remote device.
546         */
547        public void receiveSessionModifyResponse(int status,
548                VideoProfile requestedProfile, VideoProfile responseProfile) {
549            if (mVideoCallback != null) {
550                try {
551                    mVideoCallback.receiveSessionModifyResponse(
552                            status, requestedProfile, responseProfile);
553                } catch (RemoteException ignored) {
554                }
555            }
556        }
557
558        /**
559         * Invokes callback method defined in In-Call UI.
560         *
561         * Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
562         * {@link VideoProvider#SESSION_EVENT_RX_RESUME},
563         * {@link VideoProvider#SESSION_EVENT_TX_START},
564         * {@link VideoProvider#SESSION_EVENT_TX_STOP}
565         *
566         * @param event The event.
567         */
568        public void handleCallSessionEvent(int event) {
569            if (mVideoCallback != null) {
570                try {
571                    mVideoCallback.handleCallSessionEvent(event);
572                } catch (RemoteException ignored) {
573                }
574            }
575        }
576
577        /**
578         * Invokes callback method defined in In-Call UI.
579         *
580         * @param width  The updated peer video width.
581         * @param height The updated peer video height.
582         */
583        public void changePeerDimensions(int width, int height) {
584            if (mVideoCallback != null) {
585                try {
586                    mVideoCallback.changePeerDimensions(width, height);
587                } catch (RemoteException ignored) {
588                }
589            }
590        }
591
592        /**
593         * Invokes callback method defined in In-Call UI.
594         *
595         * @param dataUsage The updated data usage.
596         */
597        public void changeCallDataUsage(int dataUsage) {
598            if (mVideoCallback != null) {
599                try {
600                    mVideoCallback.changeCallDataUsage(dataUsage);
601                } catch (RemoteException ignored) {
602                }
603            }
604        }
605
606        /**
607         * Invokes callback method defined in In-Call UI.
608         *
609         * @param cameraCapabilities The changed camera capabilities.
610         */
611        public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
612            if (mVideoCallback != null) {
613                try {
614                    mVideoCallback.changeCameraCapabilities(cameraCapabilities);
615                } catch (RemoteException ignored) {
616                }
617            }
618        }
619    }
620
621    private final Listener mConnectionDeathListener = new Listener() {
622        @Override
623        public void onDestroyed(Connection c) {
624            if (mConferenceables.remove(c)) {
625                fireOnConferenceableConnectionsChanged();
626            }
627        }
628    };
629
630    private final Conference.Listener mConferenceDeathListener = new Conference.Listener() {
631        @Override
632        public void onDestroyed(Conference c) {
633            if (mConferenceables.remove(c)) {
634                fireOnConferenceableConnectionsChanged();
635            }
636        }
637    };
638
639    /**
640     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
641     * load factor before resizing, 1 means we only expect a single thread to
642     * access the map so make only a single shard
643     */
644    private final Set<Listener> mListeners = Collections.newSetFromMap(
645            new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
646    private final List<IConferenceable> mConferenceables = new ArrayList<>();
647    private final List<IConferenceable> mUnmodifiableConferenceables =
648            Collections.unmodifiableList(mConferenceables);
649
650    private int mState = STATE_NEW;
651    private AudioState mAudioState;
652    private Uri mAddress;
653    private int mAddressPresentation;
654    private String mCallerDisplayName;
655    private int mCallerDisplayNamePresentation;
656    private boolean mRingbackRequested = false;
657    private int mConnectionCapabilities;
658    private VideoProvider mVideoProvider;
659    private boolean mAudioModeIsVoip;
660    private StatusHints mStatusHints;
661    private int mVideoState;
662    private DisconnectCause mDisconnectCause;
663    private Conference mConference;
664    private ConnectionService mConnectionService;
665
666    /**
667     * Create a new Connection.
668     */
669    public Connection() {}
670
671    /**
672     * @return The address (e.g., phone number) to which this Connection is currently communicating.
673     */
674    public final Uri getAddress() {
675        return mAddress;
676    }
677
678    /**
679     * @return The presentation requirements for the address.
680     *         See {@link TelecomManager} for valid values.
681     */
682    public final int getAddressPresentation() {
683        return mAddressPresentation;
684    }
685
686    /**
687     * @return The caller display name (CNAP).
688     */
689    public final String getCallerDisplayName() {
690        return mCallerDisplayName;
691    }
692
693    /**
694     * @return The presentation requirements for the handle.
695     *         See {@link TelecomManager} for valid values.
696     */
697    public final int getCallerDisplayNamePresentation() {
698        return mCallerDisplayNamePresentation;
699    }
700
701    /**
702     * @return The state of this Connection.
703     */
704    public final int getState() {
705        return mState;
706    }
707
708    /**
709     * Returns the video state of the connection.
710     * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
711     * {@link VideoProfile.VideoState#BIDIRECTIONAL},
712     * {@link VideoProfile.VideoState#TX_ENABLED},
713     * {@link VideoProfile.VideoState#RX_ENABLED}.
714     *
715     * @return The video state of the connection.
716     * @hide
717     */
718    public final int getVideoState() {
719        return mVideoState;
720    }
721
722    /**
723     * @return The audio state of the connection, describing how its audio is currently
724     *         being routed by the system. This is {@code null} if this Connection
725     *         does not directly know about its audio state.
726     */
727    public final AudioState getAudioState() {
728        return mAudioState;
729    }
730
731    /**
732     * @return The conference that this connection is a part of.  Null if it is not part of any
733     *         conference.
734     */
735    public final Conference getConference() {
736        return mConference;
737    }
738
739    /**
740     * Returns whether this connection is requesting that the system play a ringback tone
741     * on its behalf.
742     */
743    public final boolean isRingbackRequested() {
744        return mRingbackRequested;
745    }
746
747    /**
748     * @return True if the connection's audio mode is VOIP.
749     */
750    public final boolean getAudioModeIsVoip() {
751        return mAudioModeIsVoip;
752    }
753
754    /**
755     * @return The status hints for this connection.
756     */
757    public final StatusHints getStatusHints() {
758        return mStatusHints;
759    }
760
761    /**
762     * Assign a listener to be notified of state changes.
763     *
764     * @param l A listener.
765     * @return This Connection.
766     *
767     * @hide
768     */
769    public final Connection addConnectionListener(Listener l) {
770        mListeners.add(l);
771        return this;
772    }
773
774    /**
775     * Remove a previously assigned listener that was being notified of state changes.
776     *
777     * @param l A Listener.
778     * @return This Connection.
779     *
780     * @hide
781     */
782    public final Connection removeConnectionListener(Listener l) {
783        if (l != null) {
784            mListeners.remove(l);
785        }
786        return this;
787    }
788
789    /**
790     * @return The {@link DisconnectCause} for this connection.
791     */
792    public final DisconnectCause getDisconnectCause() {
793        return mDisconnectCause;
794    }
795
796    /**
797     * Inform this Connection that the state of its audio output has been changed externally.
798     *
799     * @param state The new audio state.
800     * @hide
801     */
802    final void setAudioState(AudioState state) {
803        checkImmutable();
804        Log.d(this, "setAudioState %s", state);
805        mAudioState = state;
806        onAudioStateChanged(state);
807    }
808
809    /**
810     * @param state An integer value of a {@code STATE_*} constant.
811     * @return A string representation of the value.
812     */
813    public static String stateToString(int state) {
814        switch (state) {
815            case STATE_INITIALIZING:
816                return "STATE_INITIALIZING";
817            case STATE_NEW:
818                return "STATE_NEW";
819            case STATE_RINGING:
820                return "STATE_RINGING";
821            case STATE_DIALING:
822                return "STATE_DIALING";
823            case STATE_ACTIVE:
824                return "STATE_ACTIVE";
825            case STATE_HOLDING:
826                return "STATE_HOLDING";
827            case STATE_DISCONNECTED:
828                return "DISCONNECTED";
829            default:
830                Log.wtf(Connection.class, "Unknown state %d", state);
831                return "UNKNOWN";
832        }
833    }
834
835    /**
836     * Returns the connection's capabilities, as a bit mask of the {@code CAPABILITY_*} constants.
837     */
838    public final int getConnectionCapabilities() {
839        return mConnectionCapabilities;
840    }
841
842    /** @hide */
843    @SystemApi @Deprecated public final int getCallCapabilities() {
844        return getConnectionCapabilities();
845    }
846
847    /**
848     * Sets the value of the {@link #getAddress()} property.
849     *
850     * @param address The new address.
851     * @param presentation The presentation requirements for the address.
852     *        See {@link TelecomManager} for valid values.
853     */
854    public final void setAddress(Uri address, int presentation) {
855        checkImmutable();
856        Log.d(this, "setAddress %s", address);
857        mAddress = address;
858        mAddressPresentation = presentation;
859        for (Listener l : mListeners) {
860            l.onAddressChanged(this, address, presentation);
861        }
862    }
863
864    /**
865     * Sets the caller display name (CNAP).
866     *
867     * @param callerDisplayName The new display name.
868     * @param presentation The presentation requirements for the handle.
869     *        See {@link TelecomManager} for valid values.
870     */
871    public final void setCallerDisplayName(String callerDisplayName, int presentation) {
872        checkImmutable();
873        Log.d(this, "setCallerDisplayName %s", callerDisplayName);
874        mCallerDisplayName = callerDisplayName;
875        mCallerDisplayNamePresentation = presentation;
876        for (Listener l : mListeners) {
877            l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
878        }
879    }
880
881    /**
882     * Set the video state for the connection.
883     * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
884     * {@link VideoProfile.VideoState#BIDIRECTIONAL},
885     * {@link VideoProfile.VideoState#TX_ENABLED},
886     * {@link VideoProfile.VideoState#RX_ENABLED}.
887     *
888     * @param videoState The new video state.
889     * @hide
890     */
891    public final void setVideoState(int videoState) {
892        checkImmutable();
893        Log.d(this, "setVideoState %d", videoState);
894        mVideoState = videoState;
895        for (Listener l : mListeners) {
896            l.onVideoStateChanged(this, mVideoState);
897        }
898    }
899
900    /**
901     * Sets state to active (e.g., an ongoing connection where two or more parties can actively
902     * communicate).
903     */
904    public final void setActive() {
905        checkImmutable();
906        setRingbackRequested(false);
907        setState(STATE_ACTIVE);
908    }
909
910    /**
911     * Sets state to ringing (e.g., an inbound ringing connection).
912     */
913    public final void setRinging() {
914        checkImmutable();
915        setState(STATE_RINGING);
916    }
917
918    /**
919     * Sets state to initializing (this Connection is not yet ready to be used).
920     */
921    public final void setInitializing() {
922        checkImmutable();
923        setState(STATE_INITIALIZING);
924    }
925
926    /**
927     * Sets state to initialized (the Connection has been set up and is now ready to be used).
928     */
929    public final void setInitialized() {
930        checkImmutable();
931        setState(STATE_NEW);
932    }
933
934    /**
935     * Sets state to dialing (e.g., dialing an outbound connection).
936     */
937    public final void setDialing() {
938        checkImmutable();
939        setState(STATE_DIALING);
940    }
941
942    /**
943     * Sets state to be on hold.
944     */
945    public final void setOnHold() {
946        checkImmutable();
947        setState(STATE_HOLDING);
948    }
949
950    /**
951     * Sets the video connection provider.
952     * @param videoProvider The video provider.
953     * @hide
954     */
955    public final void setVideoProvider(VideoProvider videoProvider) {
956        checkImmutable();
957        mVideoProvider = videoProvider;
958        for (Listener l : mListeners) {
959            l.onVideoProviderChanged(this, videoProvider);
960        }
961    }
962
963    /** @hide */
964    public final VideoProvider getVideoProvider() {
965        return mVideoProvider;
966    }
967
968    /**
969     * Sets state to disconnected.
970     *
971     * @param disconnectCause The reason for the disconnection, as specified by
972     *         {@link DisconnectCause}.
973     */
974    public final void setDisconnected(DisconnectCause disconnectCause) {
975        checkImmutable();
976        mDisconnectCause = disconnectCause;
977        setState(STATE_DISCONNECTED);
978        Log.d(this, "Disconnected with cause %s", disconnectCause);
979        for (Listener l : mListeners) {
980            l.onDisconnected(this, disconnectCause);
981        }
982    }
983
984    /**
985     * Informs listeners that this {@code Connection} is in a post-dial wait state. This is done
986     * when (a) the {@code Connection} is issuing a DTMF sequence; (b) it has encountered a "wait"
987     * character; and (c) it wishes to inform the In-Call app that it is waiting for the end-user
988     * to send an {@link #onPostDialContinue(boolean)} signal.
989     *
990     * @param remaining The DTMF character sequence remaining to be emitted once the
991     *         {@link #onPostDialContinue(boolean)} is received, including any "wait" characters
992     *         that remaining sequence may contain.
993     */
994    public final void setPostDialWait(String remaining) {
995        checkImmutable();
996        for (Listener l : mListeners) {
997            l.onPostDialWait(this, remaining);
998        }
999    }
1000
1001    /**
1002     * Informs listeners that this {@code Connection} has processed a character in the post-dial
1003     * started state. This is done when (a) the {@code Connection} is issuing a DTMF sequence;
1004     * (b) it has encountered a "wait" character; and (c) it wishes to signal Telecom to play
1005     * the corresponding DTMF tone locally.
1006     *
1007     * @param nextChar The DTMF character that was just processed by the {@code Connection}.
1008     *
1009     * @hide
1010     */
1011    public final void setNextPostDialWaitChar(char nextChar) {
1012        checkImmutable();
1013        for (Listener l : mListeners) {
1014            l.onPostDialChar(this, nextChar);
1015        }
1016    }
1017
1018    /**
1019     * Requests that the framework play a ringback tone. This is to be invoked by implementations
1020     * that do not play a ringback tone themselves in the connection's audio stream.
1021     *
1022     * @param ringback Whether the ringback tone is to be played.
1023     */
1024    public final void setRingbackRequested(boolean ringback) {
1025        checkImmutable();
1026        if (mRingbackRequested != ringback) {
1027            mRingbackRequested = ringback;
1028            for (Listener l : mListeners) {
1029                l.onRingbackRequested(this, ringback);
1030            }
1031        }
1032    }
1033
1034    /** @hide */
1035    @SystemApi @Deprecated public final void setCallCapabilities(int connectionCapabilities) {
1036        setConnectionCapabilities(connectionCapabilities);
1037    }
1038
1039    /**
1040     * Sets the connection's capabilities as a bit mask of the {@code CAPABILITY_*} constants.
1041     *
1042     * @param connectionCapabilities The new connection capabilities.
1043     */
1044    public final void setConnectionCapabilities(int connectionCapabilities) {
1045        checkImmutable();
1046        if (mConnectionCapabilities != connectionCapabilities) {
1047            mConnectionCapabilities = connectionCapabilities;
1048            for (Listener l : mListeners) {
1049                l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
1050            }
1051        }
1052    }
1053
1054    /**
1055     * Tears down the Connection object.
1056     */
1057    public final void destroy() {
1058        for (Listener l : mListeners) {
1059            l.onDestroyed(this);
1060        }
1061    }
1062
1063    /**
1064     * Requests that the framework use VOIP audio mode for this connection.
1065     *
1066     * @param isVoip True if the audio mode is VOIP.
1067     */
1068    public final void setAudioModeIsVoip(boolean isVoip) {
1069        checkImmutable();
1070        mAudioModeIsVoip = isVoip;
1071        for (Listener l : mListeners) {
1072            l.onAudioModeIsVoipChanged(this, isVoip);
1073        }
1074    }
1075
1076    /**
1077     * Sets the label and icon status to display in the in-call UI.
1078     *
1079     * @param statusHints The status label and icon to set.
1080     */
1081    public final void setStatusHints(StatusHints statusHints) {
1082        checkImmutable();
1083        mStatusHints = statusHints;
1084        for (Listener l : mListeners) {
1085            l.onStatusHintsChanged(this, statusHints);
1086        }
1087    }
1088
1089    /**
1090     * Sets the connections with which this connection can be conferenced.
1091     *
1092     * @param conferenceableConnections The set of connections this connection can conference with.
1093     */
1094    public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
1095        checkImmutable();
1096        clearConferenceableList();
1097        for (Connection c : conferenceableConnections) {
1098            // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
1099            // small amount of items here.
1100            if (!mConferenceables.contains(c)) {
1101                c.addConnectionListener(mConnectionDeathListener);
1102                mConferenceables.add(c);
1103            }
1104        }
1105        fireOnConferenceableConnectionsChanged();
1106    }
1107
1108    /**
1109     * Similar to {@link #setConferenceableConnections(java.util.List)}, sets a list of connections
1110     * or conferences with which this connection can be conferenced.
1111     *
1112     * @param conferenceables The conferenceables.
1113     */
1114    public final void setConferenceables(List<IConferenceable> conferenceables) {
1115        clearConferenceableList();
1116        for (IConferenceable c : conferenceables) {
1117            // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
1118            // small amount of items here.
1119            if (!mConferenceables.contains(c)) {
1120                if (c instanceof Connection) {
1121                    Connection connection = (Connection) c;
1122                    connection.addConnectionListener(mConnectionDeathListener);
1123                } else if (c instanceof Conference) {
1124                    Conference conference = (Conference) c;
1125                    conference.addListener(mConferenceDeathListener);
1126                }
1127                mConferenceables.add(c);
1128            }
1129        }
1130        fireOnConferenceableConnectionsChanged();
1131    }
1132
1133    /**
1134     * Returns the connections or conferences with which this connection can be conferenced.
1135     */
1136    public final List<IConferenceable> getConferenceables() {
1137        return mUnmodifiableConferenceables;
1138    }
1139
1140    /*
1141     * @hide
1142     */
1143    public final void setConnectionService(ConnectionService connectionService) {
1144        checkImmutable();
1145        if (mConnectionService != null) {
1146            Log.e(this, new Exception(), "Trying to set ConnectionService on a connection " +
1147                    "which is already associated with another ConnectionService.");
1148        } else {
1149            mConnectionService = connectionService;
1150        }
1151    }
1152
1153    /**
1154     * @hide
1155     */
1156    public final void unsetConnectionService(ConnectionService connectionService) {
1157        if (mConnectionService != connectionService) {
1158            Log.e(this, new Exception(), "Trying to remove ConnectionService from a Connection " +
1159                    "that does not belong to the ConnectionService.");
1160        } else {
1161            mConnectionService = null;
1162        }
1163    }
1164
1165    /**
1166     * @hide
1167     */
1168    public final ConnectionService getConnectionService() {
1169        return mConnectionService;
1170    }
1171
1172    /**
1173     * Sets the conference that this connection is a part of. This will fail if the connection is
1174     * already part of a conference. {@link #resetConference} to un-set the conference first.
1175     *
1176     * @param conference The conference.
1177     * @return {@code true} if the conference was successfully set.
1178     * @hide
1179     */
1180    public final boolean setConference(Conference conference) {
1181        checkImmutable();
1182        // We check to see if it is already part of another conference.
1183        if (mConference == null) {
1184            mConference = conference;
1185            if (mConnectionService != null && mConnectionService.containsConference(conference)) {
1186                fireConferenceChanged();
1187            }
1188            return true;
1189        }
1190        return false;
1191    }
1192
1193    /**
1194     * Resets the conference that this connection is a part of.
1195     * @hide
1196     */
1197    public final void resetConference() {
1198        if (mConference != null) {
1199            Log.d(this, "Conference reset");
1200            mConference = null;
1201            fireConferenceChanged();
1202        }
1203    }
1204
1205    /**
1206     * Notifies this Connection that the {@link #getAudioState()} property has a new value.
1207     *
1208     * @param state The new connection audio state.
1209     */
1210    public void onAudioStateChanged(AudioState state) {}
1211
1212    /**
1213     * Notifies this Connection of an internal state change. This method is called after the
1214     * state is changed.
1215     *
1216     * @param state The new state, one of the {@code STATE_*} constants.
1217     */
1218    public void onStateChanged(int state) {}
1219
1220    /**
1221     * Notifies this Connection of a request to play a DTMF tone.
1222     *
1223     * @param c A DTMF character.
1224     */
1225    public void onPlayDtmfTone(char c) {}
1226
1227    /**
1228     * Notifies this Connection of a request to stop any currently playing DTMF tones.
1229     */
1230    public void onStopDtmfTone() {}
1231
1232    /**
1233     * Notifies this Connection of a request to disconnect.
1234     */
1235    public void onDisconnect() {}
1236
1237    /**
1238     * Notifies this Connection of a request to disconnect a participant of the conference managed
1239     * by the connection.
1240     *
1241     * @param endpoint the {@link Uri} of the participant to disconnect.
1242     * @hide
1243     */
1244    public void onDisconnectConferenceParticipant(Uri endpoint) {}
1245
1246    /**
1247     * Notifies this Connection of a request to separate from its parent conference.
1248     */
1249    public void onSeparate() {}
1250
1251    /**
1252     * Notifies this Connection of a request to abort.
1253     */
1254    public void onAbort() {}
1255
1256    /**
1257     * Notifies this Connection of a request to hold.
1258     */
1259    public void onHold() {}
1260
1261    /**
1262     * Notifies this Connection of a request to exit a hold state.
1263     */
1264    public void onUnhold() {}
1265
1266    /**
1267     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
1268     * a request to accept.
1269     *
1270     * @param videoState The video state in which to answer the connection.
1271     * @hide
1272     */
1273    public void onAnswer(int videoState) {}
1274
1275    /**
1276     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
1277     * a request to accept.
1278     */
1279    public void onAnswer() {
1280        onAnswer(VideoProfile.VideoState.AUDIO_ONLY);
1281    }
1282
1283    /**
1284     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
1285     * a request to reject.
1286     */
1287    public void onReject() {}
1288
1289    /**
1290     * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
1291     */
1292    public void onPostDialContinue(boolean proceed) {}
1293
1294    static String toLogSafePhoneNumber(String number) {
1295        // For unknown number, log empty string.
1296        if (number == null) {
1297            return "";
1298        }
1299
1300        if (PII_DEBUG) {
1301            // When PII_DEBUG is true we emit PII.
1302            return number;
1303        }
1304
1305        // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
1306        // sanitized phone numbers.
1307        StringBuilder builder = new StringBuilder();
1308        for (int i = 0; i < number.length(); i++) {
1309            char c = number.charAt(i);
1310            if (c == '-' || c == '@' || c == '.') {
1311                builder.append(c);
1312            } else {
1313                builder.append('x');
1314            }
1315        }
1316        return builder.toString();
1317    }
1318
1319    private void setState(int state) {
1320        checkImmutable();
1321        if (mState == STATE_DISCONNECTED && mState != state) {
1322            Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
1323            return;
1324        }
1325        if (mState != state) {
1326            Log.d(this, "setState: %s", stateToString(state));
1327            mState = state;
1328            onStateChanged(state);
1329            for (Listener l : mListeners) {
1330                l.onStateChanged(this, state);
1331            }
1332        }
1333    }
1334
1335    private static class FailureSignalingConnection extends Connection {
1336        private boolean mImmutable = false;
1337        public FailureSignalingConnection(DisconnectCause disconnectCause) {
1338            setDisconnected(disconnectCause);
1339            mImmutable = true;
1340        }
1341
1342        public void checkImmutable() {
1343            if (mImmutable) {
1344                throw new UnsupportedOperationException("Connection is immutable");
1345            }
1346        }
1347    }
1348
1349    /**
1350     * Return a {@code Connection} which represents a failed connection attempt. The returned
1351     * {@code Connection} will have a {@link android.telecom.DisconnectCause} and as specified,
1352     * and a {@link #getState()} of {@link #STATE_DISCONNECTED}.
1353     * <p>
1354     * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
1355     * so users of this method need not maintain a reference to its return value to destroy it.
1356     *
1357     * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
1358     * @return A {@code Connection} which indicates failure.
1359     */
1360    public static Connection createFailedConnection(DisconnectCause disconnectCause) {
1361        return new FailureSignalingConnection(disconnectCause);
1362    }
1363
1364    /**
1365     * Override to throw an {@link UnsupportedOperationException} if this {@code Connection} is
1366     * not intended to be mutated, e.g., if it is a marker for failure. Only for framework use;
1367     * this should never be un-@hide-den.
1368     *
1369     * @hide
1370     */
1371    public void checkImmutable() {}
1372
1373    /**
1374     * Return a {@code Connection} which represents a canceled connection attempt. The returned
1375     * {@code Connection} will have state {@link #STATE_DISCONNECTED}, and cannot be moved out of
1376     * that state. This connection should not be used for anything, and no other
1377     * {@code Connection}s should be attempted.
1378     * <p>
1379     * so users of this method need not maintain a reference to its return value to destroy it.
1380     *
1381     * @return A {@code Connection} which indicates that the underlying connection should
1382     * be canceled.
1383     */
1384    public static Connection createCanceledConnection() {
1385        return new FailureSignalingConnection(new DisconnectCause(DisconnectCause.CANCELED));
1386    }
1387
1388    private final void fireOnConferenceableConnectionsChanged() {
1389        for (Listener l : mListeners) {
1390            l.onConferenceablesChanged(this, getConferenceables());
1391        }
1392    }
1393
1394    private final void fireConferenceChanged() {
1395        for (Listener l : mListeners) {
1396            l.onConferenceChanged(this, mConference);
1397        }
1398    }
1399
1400    private final void clearConferenceableList() {
1401        for (IConferenceable c : mConferenceables) {
1402            if (c instanceof Connection) {
1403                Connection connection = (Connection) c;
1404                connection.removeConnectionListener(mConnectionDeathListener);
1405            } else if (c instanceof Conference) {
1406                Conference conference = (Conference) c;
1407                conference.removeListener(mConferenceDeathListener);
1408            }
1409        }
1410        mConferenceables.clear();
1411    }
1412
1413    /**
1414     * Notifies listeners of a change to conference participant(s).
1415     *
1416     * @param conferenceParticipants The participants.
1417     * @hide
1418     */
1419    protected final void updateConferenceParticipants(
1420            List<ConferenceParticipant> conferenceParticipants) {
1421        for (Listener l : mListeners) {
1422            l.onConferenceParticipantsChanged(this, conferenceParticipants);
1423        }
1424    }
1425}
1426