RemoteConnection.java revision dee56a8a79f9daa1e597f5d4f399d3a5feedcac4
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.IConnectionService;
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.RemoteException;
31import android.view.Surface;
32
33import java.util.ArrayList;
34import java.util.Collections;
35import java.util.List;
36import java.util.Set;
37import java.util.concurrent.ConcurrentHashMap;
38
39/**
40 * A connection provided to a {@link ConnectionService} by another {@code ConnectionService}
41 * running in a different process.
42 *
43 * @see ConnectionService#createRemoteOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
44 * @see ConnectionService#createRemoteIncomingConnection(PhoneAccountHandle, ConnectionRequest)
45 */
46public final class RemoteConnection {
47
48    /**
49     * Callback base class for {@link RemoteConnection}.
50     */
51    public static abstract class Callback {
52        /**
53         * Invoked when the state of this {@code RemoteConnection} has changed. See
54         * {@link #getState()}.
55         *
56         * @param connection The {@code RemoteConnection} invoking this method.
57         * @param state The new state of the {@code RemoteConnection}.
58         */
59        public void onStateChanged(RemoteConnection connection, int state) {}
60
61        /**
62         * Invoked when this {@code RemoteConnection} is disconnected.
63         *
64         * @param connection The {@code RemoteConnection} invoking this method.
65         * @param disconnectCause The ({@see DisconnectCause}) associated with this failed
66         *     connection.
67         */
68        public void onDisconnected(
69                RemoteConnection connection,
70                DisconnectCause disconnectCause) {}
71
72        /**
73         * Invoked when this {@code RemoteConnection} is requesting ringback. See
74         * {@link #isRingbackRequested()}.
75         *
76         * @param connection The {@code RemoteConnection} invoking this method.
77         * @param ringback Whether the {@code RemoteConnection} is requesting ringback.
78         */
79        public void onRingbackRequested(RemoteConnection connection, boolean ringback) {}
80
81        /**
82         * Indicates that the call capabilities of this {@code RemoteConnection} have changed.
83         * See {@link #getConnectionCapabilities()}.
84         *
85         * @param connection The {@code RemoteConnection} invoking this method.
86         * @param connectionCapabilities The new capabilities of the {@code RemoteConnection}.
87         */
88        public void onConnectionCapabilitiesChanged(
89                RemoteConnection connection,
90                int connectionCapabilities) {}
91
92        /**
93         * Invoked when the post-dial sequence in the outgoing {@code Connection} has reached a
94         * pause character. This causes the post-dial signals to stop pending user confirmation. An
95         * implementation should present this choice to the user and invoke
96         * {@link RemoteConnection#postDialContinue(boolean)} when the user makes the choice.
97         *
98         * @param connection The {@code RemoteConnection} invoking this method.
99         * @param remainingPostDialSequence The post-dial characters that remain to be sent.
100         */
101        public void onPostDialWait(RemoteConnection connection, String remainingPostDialSequence) {}
102
103        /**
104         * Invoked when the post-dial sequence in the outgoing {@code Connection} has processed
105         * a character.
106         *
107         * @param connection The {@code RemoteConnection} invoking this method.
108         * @param nextChar The character being processed.
109         */
110        public void onPostDialChar(RemoteConnection connection, char nextChar) {}
111
112        /**
113         * Indicates that the VOIP audio status of this {@code RemoteConnection} has changed.
114         * See {@link #isVoipAudioMode()}.
115         *
116         * @param connection The {@code RemoteConnection} invoking this method.
117         * @param isVoip Whether the new audio state of the {@code RemoteConnection} is VOIP.
118         */
119        public void onVoipAudioChanged(RemoteConnection connection, boolean isVoip) {}
120
121        /**
122         * Indicates that the status hints of this {@code RemoteConnection} have changed. See
123         * {@link #getStatusHints()} ()}.
124         *
125         * @param connection The {@code RemoteConnection} invoking this method.
126         * @param statusHints The new status hints of the {@code RemoteConnection}.
127         */
128        public void onStatusHintsChanged(RemoteConnection connection, StatusHints statusHints) {}
129
130        /**
131         * Indicates that the address (e.g., phone number) of this {@code RemoteConnection} has
132         * changed. See {@link #getAddress()} and {@link #getAddressPresentation()}.
133         *
134         * @param connection The {@code RemoteConnection} invoking this method.
135         * @param address The new address of the {@code RemoteConnection}.
136         * @param presentation The presentation requirements for the address.
137         *        See {@link TelecomManager} for valid values.
138         */
139        public void onAddressChanged(RemoteConnection connection, Uri address, int presentation) {}
140
141        /**
142         * Indicates that the caller display name of this {@code RemoteConnection} has changed.
143         * See {@link #getCallerDisplayName()} and {@link #getCallerDisplayNamePresentation()}.
144         *
145         * @param connection The {@code RemoteConnection} invoking this method.
146         * @param callerDisplayName The new caller display name of the {@code RemoteConnection}.
147         * @param presentation The presentation requirements for the handle.
148         *        See {@link TelecomManager} for valid values.
149         */
150        public void onCallerDisplayNameChanged(
151                RemoteConnection connection, String callerDisplayName, int presentation) {}
152
153        /**
154         * Indicates that the video state of this {@code RemoteConnection} has changed.
155         * See {@link #getVideoState()}.
156         *
157         * @param connection The {@code RemoteConnection} invoking this method.
158         * @param videoState The new video state of the {@code RemoteConnection}.
159         */
160        public void onVideoStateChanged(RemoteConnection connection, int videoState) {}
161
162        /**
163         * Indicates that this {@code RemoteConnection} has been destroyed. No further requests
164         * should be made to the {@code RemoteConnection}, and references to it should be cleared.
165         *
166         * @param connection The {@code RemoteConnection} invoking this method.
167         */
168        public void onDestroyed(RemoteConnection connection) {}
169
170        /**
171         * Indicates that the {@code RemoteConnection}s with which this {@code RemoteConnection}
172         * may be asked to create a conference has changed.
173         *
174         * @param connection The {@code RemoteConnection} invoking this method.
175         * @param conferenceableConnections The {@code RemoteConnection}s with which this
176         *         {@code RemoteConnection} may be asked to create a conference.
177         */
178        public void onConferenceableConnectionsChanged(
179                RemoteConnection connection,
180                List<RemoteConnection> conferenceableConnections) {}
181
182        /**
183         * Indicates that the {@code VideoProvider} associated with this {@code RemoteConnection}
184         * has changed.
185         *
186         * @param connection The {@code RemoteConnection} invoking this method.
187         * @param videoProvider The new {@code VideoProvider} associated with this
188         *         {@code RemoteConnection}.
189         */
190        public void onVideoProviderChanged(
191                RemoteConnection connection, VideoProvider videoProvider) {}
192
193        /**
194         * Indicates that the {@code RemoteConference} that this {@code RemoteConnection} is a part
195         * of has changed.
196         *
197         * @param connection The {@code RemoteConnection} invoking this method.
198         * @param conference The {@code RemoteConference} of which this {@code RemoteConnection} is
199         *         a part, which may be {@code null}.
200         */
201        public void onConferenceChanged(
202                RemoteConnection connection,
203                RemoteConference conference) {}
204
205        /**
206         * Handles changes to the {@code RemoteConnection} extras.
207         *
208         * @param connection The {@code RemoteConnection} invoking this method.
209         * @param extras The extras containing other information associated with the connection.
210         */
211        public void onExtrasChanged(RemoteConnection connection, @Nullable Bundle extras) {}
212
213        /**
214         * Handles a connection event propagated to this {@link RemoteConnection}.
215         * <p>
216         * Connection events originate from {@link Connection#sendConnectionEvent(String, Bundle)}.
217         *
218         * @param connection The {@code RemoteConnection} invoking this method.
219         * @param event The connection event.
220         * @param extras Extras associated with the event.
221         */
222        public void onConnectionEvent(RemoteConnection connection, String event, Bundle extras) {}
223    }
224
225    /**
226     * {@link RemoteConnection.VideoProvider} associated with a {@link RemoteConnection}.  Used to
227     * receive video related events and control the video associated with a
228     * {@link RemoteConnection}.
229     *
230     * @see Connection.VideoProvider
231     */
232    public static class VideoProvider {
233
234        /**
235         * Callback class used by the {@link RemoteConnection.VideoProvider} to relay events from
236         * the {@link Connection.VideoProvider}.
237         */
238        public abstract static class Callback {
239            /**
240             * Reports a session modification request received from the
241             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
242             *
243             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
244             * @param videoProfile The requested video call profile.
245             * @see InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)
246             * @see Connection.VideoProvider#receiveSessionModifyRequest(VideoProfile)
247             */
248            public void onSessionModifyRequestReceived(
249                    VideoProvider videoProvider,
250                    VideoProfile videoProfile) {}
251
252            /**
253             * Reports a session modification response received from the
254             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
255             *
256             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
257             * @param status Status of the session modify request.
258             * @param requestedProfile The original request which was sent to the peer device.
259             * @param responseProfile The actual profile changes made by the peer device.
260             * @see InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
261             *      VideoProfile, VideoProfile)
262             * @see Connection.VideoProvider#receiveSessionModifyResponse(int, VideoProfile,
263             *      VideoProfile)
264             */
265            public void onSessionModifyResponseReceived(
266                    VideoProvider videoProvider,
267                    int status,
268                    VideoProfile requestedProfile,
269                    VideoProfile responseProfile) {}
270
271            /**
272             * Reports a call session event received from the {@link Connection.VideoProvider}
273             * associated with a {@link RemoteConnection}.
274             *
275             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
276             * @param event The event.
277             * @see InCallService.VideoCall.Callback#onCallSessionEvent(int)
278             * @see Connection.VideoProvider#handleCallSessionEvent(int)
279             */
280            public void onCallSessionEvent(VideoProvider videoProvider, int event) {}
281
282            /**
283             * Reports a change in the peer video dimensions received from the
284             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
285             *
286             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
287             * @param width  The updated peer video width.
288             * @param height The updated peer video height.
289             * @see InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)
290             * @see Connection.VideoProvider#changePeerDimensions(int, int)
291             */
292            public void onPeerDimensionsChanged(VideoProvider videoProvider, int width,
293                    int height) {}
294
295            /**
296             * Reports a change in the data usage (in bytes) received from the
297             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
298             *
299             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
300             * @param dataUsage The updated data usage (in bytes).
301             * @see InCallService.VideoCall.Callback#onCallDataUsageChanged(long)
302             * @see Connection.VideoProvider#setCallDataUsage(long)
303             */
304            public void onCallDataUsageChanged(VideoProvider videoProvider, long dataUsage) {}
305
306            /**
307             * Reports a change in the capabilities of the current camera, received from the
308             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
309             *
310             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
311             * @param cameraCapabilities The changed camera capabilities.
312             * @see InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
313             *      VideoProfile.CameraCapabilities)
314             * @see Connection.VideoProvider#changeCameraCapabilities(
315             *      VideoProfile.CameraCapabilities)
316             */
317            public void onCameraCapabilitiesChanged(
318                    VideoProvider videoProvider,
319                    VideoProfile.CameraCapabilities cameraCapabilities) {}
320
321            /**
322             * Reports a change in the video quality received from the
323             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
324             *
325             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
326             * @param videoQuality  The updated peer video quality.
327             * @see InCallService.VideoCall.Callback#onVideoQualityChanged(int)
328             * @see Connection.VideoProvider#changeVideoQuality(int)
329             */
330            public void onVideoQualityChanged(VideoProvider videoProvider, int videoQuality) {}
331        }
332
333        private final IVideoCallback mVideoCallbackDelegate = new IVideoCallback() {
334            @Override
335            public void receiveSessionModifyRequest(VideoProfile videoProfile) {
336                for (Callback l : mCallbacks) {
337                    l.onSessionModifyRequestReceived(VideoProvider.this, videoProfile);
338                }
339            }
340
341            @Override
342            public void receiveSessionModifyResponse(int status, VideoProfile requestedProfile,
343                    VideoProfile responseProfile) {
344                for (Callback l : mCallbacks) {
345                    l.onSessionModifyResponseReceived(
346                            VideoProvider.this,
347                            status,
348                            requestedProfile,
349                            responseProfile);
350                }
351            }
352
353            @Override
354            public void handleCallSessionEvent(int event) {
355                for (Callback l : mCallbacks) {
356                    l.onCallSessionEvent(VideoProvider.this, event);
357                }
358            }
359
360            @Override
361            public void changePeerDimensions(int width, int height) {
362                for (Callback l : mCallbacks) {
363                    l.onPeerDimensionsChanged(VideoProvider.this, width, height);
364                }
365            }
366
367            @Override
368            public void changeCallDataUsage(long dataUsage) {
369                for (Callback l : mCallbacks) {
370                    l.onCallDataUsageChanged(VideoProvider.this, dataUsage);
371                }
372            }
373
374            @Override
375            public void changeCameraCapabilities(
376                    VideoProfile.CameraCapabilities cameraCapabilities) {
377                for (Callback l : mCallbacks) {
378                    l.onCameraCapabilitiesChanged(VideoProvider.this, cameraCapabilities);
379                }
380            }
381
382            @Override
383            public void changeVideoQuality(int videoQuality) {
384                for (Callback l : mCallbacks) {
385                    l.onVideoQualityChanged(VideoProvider.this, videoQuality);
386                }
387            }
388
389            @Override
390            public IBinder asBinder() {
391                return null;
392            }
393        };
394
395        private final VideoCallbackServant mVideoCallbackServant =
396                new VideoCallbackServant(mVideoCallbackDelegate);
397
398        private final IVideoProvider mVideoProviderBinder;
399
400        /**
401         * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
402         * load factor before resizing, 1 means we only expect a single thread to
403         * access the map so make only a single shard
404         */
405        private final Set<Callback> mCallbacks = Collections.newSetFromMap(
406                new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
407
408        VideoProvider(IVideoProvider videoProviderBinder) {
409            mVideoProviderBinder = videoProviderBinder;
410            try {
411                mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder());
412            } catch (RemoteException e) {
413            }
414        }
415
416        /**
417         * Registers a callback to receive commands and state changes for video calls.
418         *
419         * @param l The video call callback.
420         */
421        public void registerCallback(Callback l) {
422            mCallbacks.add(l);
423        }
424
425        /**
426         * Clears the video call callback set via {@link #registerCallback}.
427         *
428         * @param l The video call callback to clear.
429         */
430        public void unregisterCallback(Callback l) {
431            mCallbacks.remove(l);
432        }
433
434        /**
435         * Sets the camera to be used for the outgoing video for the
436         * {@link RemoteConnection.VideoProvider}.
437         *
438         * @param cameraId The id of the camera (use ids as reported by
439         * {@link CameraManager#getCameraIdList()}).
440         * @see Connection.VideoProvider#onSetCamera(String)
441         */
442        public void setCamera(String cameraId) {
443            try {
444                mVideoProviderBinder.setCamera(cameraId);
445            } catch (RemoteException e) {
446            }
447        }
448
449        /**
450         * Sets the surface to be used for displaying a preview of what the user's camera is
451         * currently capturing for the {@link RemoteConnection.VideoProvider}.
452         *
453         * @param surface The {@link Surface}.
454         * @see Connection.VideoProvider#onSetPreviewSurface(Surface)
455         */
456        public void setPreviewSurface(Surface surface) {
457            try {
458                mVideoProviderBinder.setPreviewSurface(surface);
459            } catch (RemoteException e) {
460            }
461        }
462
463        /**
464         * Sets the surface to be used for displaying the video received from the remote device for
465         * the {@link RemoteConnection.VideoProvider}.
466         *
467         * @param surface The {@link Surface}.
468         * @see Connection.VideoProvider#onSetDisplaySurface(Surface)
469         */
470        public void setDisplaySurface(Surface surface) {
471            try {
472                mVideoProviderBinder.setDisplaySurface(surface);
473            } catch (RemoteException e) {
474            }
475        }
476
477        /**
478         * Sets the device orientation, in degrees, for the {@link RemoteConnection.VideoProvider}.
479         * Assumes that a standard portrait orientation of the device is 0 degrees.
480         *
481         * @param rotation The device orientation, in degrees.
482         * @see Connection.VideoProvider#onSetDeviceOrientation(int)
483         */
484        public void setDeviceOrientation(int rotation) {
485            try {
486                mVideoProviderBinder.setDeviceOrientation(rotation);
487            } catch (RemoteException e) {
488            }
489        }
490
491        /**
492         * Sets camera zoom ratio for the {@link RemoteConnection.VideoProvider}.
493         *
494         * @param value The camera zoom ratio.
495         * @see Connection.VideoProvider#onSetZoom(float)
496         */
497        public void setZoom(float value) {
498            try {
499                mVideoProviderBinder.setZoom(value);
500            } catch (RemoteException e) {
501            }
502        }
503
504        /**
505         * Issues a request to modify the properties of the current video session for the
506         * {@link RemoteConnection.VideoProvider}.
507         *
508         * @param fromProfile The video profile prior to the request.
509         * @param toProfile The video profile with the requested changes made.
510         * @see Connection.VideoProvider#onSendSessionModifyRequest(VideoProfile, VideoProfile)
511         */
512        public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
513            try {
514                mVideoProviderBinder.sendSessionModifyRequest(fromProfile, toProfile);
515            } catch (RemoteException e) {
516            }
517        }
518
519        /**
520         * Provides a response to a request to change the current call video session
521         * properties for the {@link RemoteConnection.VideoProvider}.
522         *
523         * @param responseProfile The response call video properties.
524         * @see Connection.VideoProvider#onSendSessionModifyResponse(VideoProfile)
525         */
526        public void sendSessionModifyResponse(VideoProfile responseProfile) {
527            try {
528                mVideoProviderBinder.sendSessionModifyResponse(responseProfile);
529            } catch (RemoteException e) {
530            }
531        }
532
533        /**
534         * Issues a request to retrieve the capabilities of the current camera for the
535         * {@link RemoteConnection.VideoProvider}.
536         *
537         * @see Connection.VideoProvider#onRequestCameraCapabilities()
538         */
539        public void requestCameraCapabilities() {
540            try {
541                mVideoProviderBinder.requestCameraCapabilities();
542            } catch (RemoteException e) {
543            }
544        }
545
546        /**
547         * Issues a request to retrieve the data usage (in bytes) of the video portion of the
548         * {@link RemoteConnection} for the {@link RemoteConnection.VideoProvider}.
549         *
550         * @see Connection.VideoProvider#onRequestConnectionDataUsage()
551         */
552        public void requestCallDataUsage() {
553            try {
554                mVideoProviderBinder.requestCallDataUsage();
555            } catch (RemoteException e) {
556            }
557        }
558
559        /**
560         * Sets the {@link Uri} of an image to be displayed to the peer device when the video signal
561         * is paused, for the {@link RemoteConnection.VideoProvider}.
562         *
563         * @see Connection.VideoProvider#onSetPauseImage(Uri)
564         */
565        public void setPauseImage(Uri uri) {
566            try {
567                mVideoProviderBinder.setPauseImage(uri);
568            } catch (RemoteException e) {
569            }
570        }
571    }
572
573    private IConnectionService mConnectionService;
574    private final String mConnectionId;
575    /**
576     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
577     * load factor before resizing, 1 means we only expect a single thread to
578     * access the map so make only a single shard
579     */
580    private final Set<CallbackRecord> mCallbackRecords = Collections.newSetFromMap(
581            new ConcurrentHashMap<CallbackRecord, Boolean>(8, 0.9f, 1));
582    private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
583    private final List<RemoteConnection> mUnmodifiableconferenceableConnections =
584            Collections.unmodifiableList(mConferenceableConnections);
585
586    private int mState = Connection.STATE_NEW;
587    private DisconnectCause mDisconnectCause;
588    private boolean mRingbackRequested;
589    private boolean mConnected;
590    private int mConnectionCapabilities;
591    private int mVideoState;
592    private VideoProvider mVideoProvider;
593    private boolean mIsVoipAudioMode;
594    private StatusHints mStatusHints;
595    private Uri mAddress;
596    private int mAddressPresentation;
597    private String mCallerDisplayName;
598    private int mCallerDisplayNamePresentation;
599    private RemoteConference mConference;
600    private Bundle mExtras;
601
602    /**
603     * @hide
604     */
605    RemoteConnection(
606            String id,
607            IConnectionService connectionService,
608            ConnectionRequest request) {
609        mConnectionId = id;
610        mConnectionService = connectionService;
611        mConnected = true;
612        mState = Connection.STATE_INITIALIZING;
613    }
614
615    /**
616     * @hide
617     */
618    RemoteConnection(String callId, IConnectionService connectionService,
619            ParcelableConnection connection) {
620        mConnectionId = callId;
621        mConnectionService = connectionService;
622        mConnected = true;
623        mState = connection.getState();
624        mDisconnectCause = connection.getDisconnectCause();
625        mRingbackRequested = connection.isRingbackRequested();
626        mConnectionCapabilities = connection.getConnectionCapabilities();
627        mVideoState = connection.getVideoState();
628        mVideoProvider = new RemoteConnection.VideoProvider(connection.getVideoProvider());
629        mIsVoipAudioMode = connection.getIsVoipAudioMode();
630        mStatusHints = connection.getStatusHints();
631        mAddress = connection.getHandle();
632        mAddressPresentation = connection.getHandlePresentation();
633        mCallerDisplayName = connection.getCallerDisplayName();
634        mCallerDisplayNamePresentation = connection.getCallerDisplayNamePresentation();
635        mConference = null;
636    }
637
638    /**
639     * Create a RemoteConnection which is used for failed connections. Note that using it for any
640     * "real" purpose will almost certainly fail. Callers should note the failure and act
641     * accordingly (moving on to another RemoteConnection, for example)
642     *
643     * @param disconnectCause The reason for the failed connection.
644     * @hide
645     */
646    RemoteConnection(DisconnectCause disconnectCause) {
647        mConnectionId = "NULL";
648        mConnected = false;
649        mState = Connection.STATE_DISCONNECTED;
650        mDisconnectCause = disconnectCause;
651    }
652
653    /**
654     * Adds a callback to this {@code RemoteConnection}.
655     *
656     * @param callback A {@code Callback}.
657     */
658    public void registerCallback(Callback callback) {
659        registerCallback(callback, new Handler());
660    }
661
662    /**
663     * Adds a callback to this {@code RemoteConnection}.
664     *
665     * @param callback A {@code Callback}.
666     * @param handler A {@code Handler} which command and status changes will be delivered to.
667     */
668    public void registerCallback(Callback callback, Handler handler) {
669        unregisterCallback(callback);
670        if (callback != null && handler != null) {
671            mCallbackRecords.add(new CallbackRecord(callback, handler));
672        }
673    }
674
675    /**
676     * Removes a callback from this {@code RemoteConnection}.
677     *
678     * @param callback A {@code Callback}.
679     */
680    public void unregisterCallback(Callback callback) {
681        if (callback != null) {
682            for (CallbackRecord record : mCallbackRecords) {
683                if (record.getCallback() == callback) {
684                    mCallbackRecords.remove(record);
685                    break;
686                }
687            }
688        }
689    }
690
691    /**
692     * Obtains the state of this {@code RemoteConnection}.
693     *
694     * @return A state value, chosen from the {@code STATE_*} constants.
695     */
696    public int getState() {
697        return mState;
698    }
699
700    /**
701     * Obtains the reason why this {@code RemoteConnection} may have been disconnected.
702     *
703     * @return For a {@link Connection#STATE_DISCONNECTED} {@code RemoteConnection}, the
704     *         disconnect cause expressed as a code chosen from among those declared in
705     *         {@link DisconnectCause}.
706     */
707    public DisconnectCause getDisconnectCause() {
708        return mDisconnectCause;
709    }
710
711    /**
712     * Obtains the capabilities of this {@code RemoteConnection}.
713     *
714     * @return A bitmask of the capabilities of the {@code RemoteConnection}, as defined in
715     *         the {@code CAPABILITY_*} constants in class {@link Connection}.
716     */
717    public int getConnectionCapabilities() {
718        return mConnectionCapabilities;
719    }
720
721    /**
722     * Determines if the audio mode of this {@code RemoteConnection} is VOIP.
723     *
724     * @return {@code true} if the {@code RemoteConnection}'s current audio mode is VOIP.
725     */
726    public boolean isVoipAudioMode() {
727        return mIsVoipAudioMode;
728    }
729
730    /**
731     * Obtains status hints pertaining to this {@code RemoteConnection}.
732     *
733     * @return The current {@link StatusHints} of this {@code RemoteConnection},
734     *         or {@code null} if none have been set.
735     */
736    public StatusHints getStatusHints() {
737        return mStatusHints;
738    }
739
740    /**
741     * Obtains the address of this {@code RemoteConnection}.
742     *
743     * @return The address (e.g., phone number) to which the {@code RemoteConnection}
744     *         is currently connected.
745     */
746    public Uri getAddress() {
747        return mAddress;
748    }
749
750    /**
751     * Obtains the presentation requirements for the address of this {@code RemoteConnection}.
752     *
753     * @return The presentation requirements for the address. See
754     *         {@link TelecomManager} for valid values.
755     */
756    public int getAddressPresentation() {
757        return mAddressPresentation;
758    }
759
760    /**
761     * Obtains the display name for this {@code RemoteConnection}'s caller.
762     *
763     * @return The display name for the caller.
764     */
765    public CharSequence getCallerDisplayName() {
766        return mCallerDisplayName;
767    }
768
769    /**
770     * Obtains the presentation requirements for this {@code RemoteConnection}'s
771     * caller's display name.
772     *
773     * @return The presentation requirements for the caller display name. See
774     *         {@link TelecomManager} for valid values.
775     */
776    public int getCallerDisplayNamePresentation() {
777        return mCallerDisplayNamePresentation;
778    }
779
780    /**
781     * Obtains the video state of this {@code RemoteConnection}.
782     *
783     * @return The video state of the {@code RemoteConnection}. See {@link VideoProfile}.
784     */
785    public int getVideoState() {
786        return mVideoState;
787    }
788
789    /**
790     * Obtains the video provider of this {@code RemoteConnection}.
791     * @return The video provider associated with this {@code RemoteConnection}.
792     */
793    public final VideoProvider getVideoProvider() {
794        return mVideoProvider;
795    }
796
797    /**
798     * Obtain the extras associated with this {@code RemoteConnection}.
799     *
800     * @return The extras for this connection.
801     */
802    public final Bundle getExtras() {
803        return mExtras;
804    }
805
806    /**
807     * Determines whether this {@code RemoteConnection} is requesting ringback.
808     *
809     * @return Whether the {@code RemoteConnection} is requesting that the framework play a
810     *         ringback tone on its behalf.
811     */
812    public boolean isRingbackRequested() {
813        return mRingbackRequested;
814    }
815
816    /**
817     * Instructs this {@code RemoteConnection} to abort.
818     */
819    public void abort() {
820        try {
821            if (mConnected) {
822                mConnectionService.abort(mConnectionId);
823            }
824        } catch (RemoteException ignored) {
825        }
826    }
827
828    /**
829     * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
830     */
831    public void answer() {
832        try {
833            if (mConnected) {
834                mConnectionService.answer(mConnectionId);
835            }
836        } catch (RemoteException ignored) {
837        }
838    }
839
840    /**
841     * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
842     * @param videoState The video state in which to answer the call.
843     * @hide
844     */
845    public void answer(int videoState) {
846        try {
847            if (mConnected) {
848                mConnectionService.answerVideo(mConnectionId, videoState);
849            }
850        } catch (RemoteException ignored) {
851        }
852    }
853
854    /**
855     * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to reject.
856     */
857    public void reject() {
858        try {
859            if (mConnected) {
860                mConnectionService.reject(mConnectionId);
861            }
862        } catch (RemoteException ignored) {
863        }
864    }
865
866    /**
867     * Instructs this {@code RemoteConnection} to go on hold.
868     */
869    public void hold() {
870        try {
871            if (mConnected) {
872                mConnectionService.hold(mConnectionId);
873            }
874        } catch (RemoteException ignored) {
875        }
876    }
877
878    /**
879     * Instructs this {@link Connection#STATE_HOLDING} call to release from hold.
880     */
881    public void unhold() {
882        try {
883            if (mConnected) {
884                mConnectionService.unhold(mConnectionId);
885            }
886        } catch (RemoteException ignored) {
887        }
888    }
889
890    /**
891     * Instructs this {@code RemoteConnection} to disconnect.
892     */
893    public void disconnect() {
894        try {
895            if (mConnected) {
896                mConnectionService.disconnect(mConnectionId);
897            }
898        } catch (RemoteException ignored) {
899        }
900    }
901
902    /**
903     * Instructs this {@code RemoteConnection} to play a dual-tone multi-frequency signaling
904     * (DTMF) tone.
905     *
906     * Any other currently playing DTMF tone in the specified call is immediately stopped.
907     *
908     * @param digit A character representing the DTMF digit for which to play the tone. This
909     *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
910     */
911    public void playDtmfTone(char digit) {
912        try {
913            if (mConnected) {
914                mConnectionService.playDtmfTone(mConnectionId, digit);
915            }
916        } catch (RemoteException ignored) {
917        }
918    }
919
920    /**
921     * Instructs this {@code RemoteConnection} to stop any dual-tone multi-frequency signaling
922     * (DTMF) tone currently playing.
923     *
924     * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
925     * currently playing, this method will do nothing.
926     */
927    public void stopDtmfTone() {
928        try {
929            if (mConnected) {
930                mConnectionService.stopDtmfTone(mConnectionId);
931            }
932        } catch (RemoteException ignored) {
933        }
934    }
935
936    /**
937     * Instructs this {@code RemoteConnection} to continue playing a post-dial DTMF string.
938     *
939     * A post-dial DTMF string is a string of digits following the first instance of either
940     * {@link TelecomManager#DTMF_CHARACTER_WAIT} or {@link TelecomManager#DTMF_CHARACTER_PAUSE}.
941     * These digits are immediately sent as DTMF tones to the recipient as soon as the
942     * connection is made.
943     *
944     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
945     * {@code RemoteConnection} will temporarily pause playing the tones for a pre-defined period
946     * of time.
947     *
948     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
949     * {@code RemoteConnection} will pause playing the tones and notify callbacks via
950     * {@link Callback#onPostDialWait(RemoteConnection, String)}. At this point, the in-call app
951     * should display to the user an indication of this state and an affordance to continue
952     * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
953     * app should invoke the {@link #postDialContinue(boolean)} method.
954     *
955     * @param proceed Whether or not to continue with the post-dial sequence.
956     */
957    public void postDialContinue(boolean proceed) {
958        try {
959            if (mConnected) {
960                mConnectionService.onPostDialContinue(mConnectionId, proceed);
961            }
962        } catch (RemoteException ignored) {
963        }
964    }
965
966    /**
967     * Instructs this {@link RemoteConnection} to pull itself to the local device.
968     * <p>
969     * See {@link Call#pullExternalCall()} for more information.
970     */
971    public void pullExternalCall() {
972        try {
973            if (mConnected) {
974                mConnectionService.pullExternalCall(mConnectionId);
975            }
976        } catch (RemoteException ignored) {
977        }
978    }
979
980    /**
981     * Set the audio state of this {@code RemoteConnection}.
982     *
983     * @param state The audio state of this {@code RemoteConnection}.
984     * @hide
985     * @deprecated Use {@link #setCallAudioState(CallAudioState) instead.
986     */
987    @SystemApi
988    @Deprecated
989    public void setAudioState(AudioState state) {
990        setCallAudioState(new CallAudioState(state));
991    }
992
993    /**
994     * Set the audio state of this {@code RemoteConnection}.
995     *
996     * @param state The audio state of this {@code RemoteConnection}.
997     */
998    public void setCallAudioState(CallAudioState state) {
999        try {
1000            if (mConnected) {
1001                mConnectionService.onCallAudioStateChanged(mConnectionId, state);
1002            }
1003        } catch (RemoteException ignored) {
1004        }
1005    }
1006
1007    /**
1008     * Obtain the {@code RemoteConnection}s with which this {@code RemoteConnection} may be
1009     * successfully asked to create a conference with.
1010     *
1011     * @return The {@code RemoteConnection}s with which this {@code RemoteConnection} may be
1012     *         merged into a {@link RemoteConference}.
1013     */
1014    public List<RemoteConnection> getConferenceableConnections() {
1015        return mUnmodifiableconferenceableConnections;
1016    }
1017
1018    /**
1019     * Obtain the {@code RemoteConference} that this {@code RemoteConnection} may be a part
1020     * of, or {@code null} if there is no such {@code RemoteConference}.
1021     *
1022     * @return A {@code RemoteConference} or {@code null};
1023     */
1024    public RemoteConference getConference() {
1025        return mConference;
1026    }
1027
1028    /** {@hide} */
1029    String getId() {
1030        return mConnectionId;
1031    }
1032
1033    /** {@hide} */
1034    IConnectionService getConnectionService() {
1035        return mConnectionService;
1036    }
1037
1038    /**
1039     * @hide
1040     */
1041    void setState(final int state) {
1042        if (mState != state) {
1043            mState = state;
1044            for (CallbackRecord record: mCallbackRecords) {
1045                final RemoteConnection connection = this;
1046                final Callback callback = record.getCallback();
1047                record.getHandler().post(new Runnable() {
1048                    @Override
1049                    public void run() {
1050                        callback.onStateChanged(connection, state);
1051                    }
1052                });
1053            }
1054        }
1055    }
1056
1057    /**
1058     * @hide
1059     */
1060    void setDisconnected(final DisconnectCause disconnectCause) {
1061        if (mState != Connection.STATE_DISCONNECTED) {
1062            mState = Connection.STATE_DISCONNECTED;
1063            mDisconnectCause = disconnectCause;
1064
1065            for (CallbackRecord record : mCallbackRecords) {
1066                final RemoteConnection connection = this;
1067                final Callback callback = record.getCallback();
1068                record.getHandler().post(new Runnable() {
1069                    @Override
1070                    public void run() {
1071                        callback.onDisconnected(connection, disconnectCause);
1072                    }
1073                });
1074            }
1075        }
1076    }
1077
1078    /**
1079     * @hide
1080     */
1081    void setRingbackRequested(final boolean ringback) {
1082        if (mRingbackRequested != ringback) {
1083            mRingbackRequested = ringback;
1084            for (CallbackRecord record : mCallbackRecords) {
1085                final RemoteConnection connection = this;
1086                final Callback callback = record.getCallback();
1087                record.getHandler().post(new Runnable() {
1088                    @Override
1089                    public void run() {
1090                        callback.onRingbackRequested(connection, ringback);
1091                    }
1092                });
1093            }
1094        }
1095    }
1096
1097    /**
1098     * @hide
1099     */
1100    void setConnectionCapabilities(final int connectionCapabilities) {
1101        mConnectionCapabilities = connectionCapabilities;
1102        for (CallbackRecord record : mCallbackRecords) {
1103            final RemoteConnection connection = this;
1104            final Callback callback = record.getCallback();
1105            record.getHandler().post(new Runnable() {
1106                @Override
1107                public void run() {
1108                    callback.onConnectionCapabilitiesChanged(connection, connectionCapabilities);
1109                }
1110            });
1111        }
1112    }
1113
1114    /**
1115     * @hide
1116     */
1117    void setDestroyed() {
1118        if (!mCallbackRecords.isEmpty()) {
1119            // Make sure that the callbacks are notified that the call is destroyed first.
1120            if (mState != Connection.STATE_DISCONNECTED) {
1121                setDisconnected(
1122                        new DisconnectCause(DisconnectCause.ERROR, "Connection destroyed."));
1123            }
1124
1125            for (CallbackRecord record : mCallbackRecords) {
1126                final RemoteConnection connection = this;
1127                final Callback callback = record.getCallback();
1128                record.getHandler().post(new Runnable() {
1129                    @Override
1130                    public void run() {
1131                        callback.onDestroyed(connection);
1132                    }
1133                });
1134            }
1135            mCallbackRecords.clear();
1136
1137            mConnected = false;
1138        }
1139    }
1140
1141    /**
1142     * @hide
1143     */
1144    void setPostDialWait(final String remainingDigits) {
1145        for (CallbackRecord record : mCallbackRecords) {
1146            final RemoteConnection connection = this;
1147            final Callback callback = record.getCallback();
1148            record.getHandler().post(new Runnable() {
1149                @Override
1150                public void run() {
1151                    callback.onPostDialWait(connection, remainingDigits);
1152                }
1153            });
1154        }
1155    }
1156
1157    /**
1158     * @hide
1159     */
1160    void onPostDialChar(final char nextChar) {
1161        for (CallbackRecord record : mCallbackRecords) {
1162            final RemoteConnection connection = this;
1163            final Callback callback = record.getCallback();
1164            record.getHandler().post(new Runnable() {
1165                @Override
1166                public void run() {
1167                    callback.onPostDialChar(connection, nextChar);
1168                }
1169            });
1170        }
1171    }
1172
1173    /**
1174     * @hide
1175     */
1176    void setVideoState(final int videoState) {
1177        mVideoState = videoState;
1178        for (CallbackRecord record : mCallbackRecords) {
1179            final RemoteConnection connection = this;
1180            final Callback callback = record.getCallback();
1181            record.getHandler().post(new Runnable() {
1182                @Override
1183                public void run() {
1184                    callback.onVideoStateChanged(connection, videoState);
1185                }
1186            });
1187        }
1188    }
1189
1190    /**
1191     * @hide
1192     */
1193    void setVideoProvider(final VideoProvider videoProvider) {
1194        mVideoProvider = videoProvider;
1195        for (CallbackRecord record : mCallbackRecords) {
1196            final RemoteConnection connection = this;
1197            final Callback callback = record.getCallback();
1198            record.getHandler().post(new Runnable() {
1199                @Override
1200                public void run() {
1201                    callback.onVideoProviderChanged(connection, videoProvider);
1202                }
1203            });
1204        }
1205    }
1206
1207    /** @hide */
1208    void setIsVoipAudioMode(final boolean isVoip) {
1209        mIsVoipAudioMode = isVoip;
1210        for (CallbackRecord record : mCallbackRecords) {
1211            final RemoteConnection connection = this;
1212            final Callback callback = record.getCallback();
1213            record.getHandler().post(new Runnable() {
1214                @Override
1215                public void run() {
1216                    callback.onVoipAudioChanged(connection, isVoip);
1217                }
1218            });
1219        }
1220    }
1221
1222    /** @hide */
1223    void setStatusHints(final StatusHints statusHints) {
1224        mStatusHints = statusHints;
1225        for (CallbackRecord record : mCallbackRecords) {
1226            final RemoteConnection connection = this;
1227            final Callback callback = record.getCallback();
1228            record.getHandler().post(new Runnable() {
1229                @Override
1230                public void run() {
1231                    callback.onStatusHintsChanged(connection, statusHints);
1232                }
1233            });
1234        }
1235    }
1236
1237    /** @hide */
1238    void setAddress(final Uri address, final int presentation) {
1239        mAddress = address;
1240        mAddressPresentation = presentation;
1241        for (CallbackRecord record : mCallbackRecords) {
1242            final RemoteConnection connection = this;
1243            final Callback callback = record.getCallback();
1244            record.getHandler().post(new Runnable() {
1245                @Override
1246                public void run() {
1247                    callback.onAddressChanged(connection, address, presentation);
1248                }
1249            });
1250        }
1251    }
1252
1253    /** @hide */
1254    void setCallerDisplayName(final String callerDisplayName, final int presentation) {
1255        mCallerDisplayName = callerDisplayName;
1256        mCallerDisplayNamePresentation = presentation;
1257        for (CallbackRecord record : mCallbackRecords) {
1258            final RemoteConnection connection = this;
1259            final Callback callback = record.getCallback();
1260            record.getHandler().post(new Runnable() {
1261                @Override
1262                public void run() {
1263                    callback.onCallerDisplayNameChanged(
1264                            connection, callerDisplayName, presentation);
1265                }
1266            });
1267        }
1268    }
1269
1270    /** @hide */
1271    void setConferenceableConnections(final List<RemoteConnection> conferenceableConnections) {
1272        mConferenceableConnections.clear();
1273        mConferenceableConnections.addAll(conferenceableConnections);
1274        for (CallbackRecord record : mCallbackRecords) {
1275            final RemoteConnection connection = this;
1276            final Callback callback = record.getCallback();
1277            record.getHandler().post(new Runnable() {
1278                @Override
1279                public void run() {
1280                    callback.onConferenceableConnectionsChanged(
1281                            connection, mUnmodifiableconferenceableConnections);
1282                }
1283            });
1284        }
1285    }
1286
1287    /** @hide */
1288    void setConference(final RemoteConference conference) {
1289        if (mConference != conference) {
1290            mConference = conference;
1291            for (CallbackRecord record : mCallbackRecords) {
1292                final RemoteConnection connection = this;
1293                final Callback callback = record.getCallback();
1294                record.getHandler().post(new Runnable() {
1295                    @Override
1296                    public void run() {
1297                        callback.onConferenceChanged(connection, conference);
1298                    }
1299                });
1300            }
1301        }
1302    }
1303
1304    /** @hide */
1305    void putExtras(final Bundle extras) {
1306        if (mExtras == null) {
1307            mExtras = new Bundle();
1308        }
1309        mExtras.putAll(extras);
1310
1311        notifyExtrasChanged();
1312    }
1313
1314    /** @hide */
1315    void removeExtras(List<String> keys) {
1316        if (mExtras == null || keys == null || keys.isEmpty()) {
1317            return;
1318        }
1319        for (String key : keys) {
1320            mExtras.remove(key);
1321        }
1322
1323        notifyExtrasChanged();
1324    }
1325
1326    private void notifyExtrasChanged() {
1327        for (CallbackRecord record : mCallbackRecords) {
1328            final RemoteConnection connection = this;
1329            final Callback callback = record.getCallback();
1330            record.getHandler().post(new Runnable() {
1331                @Override
1332                public void run() {
1333                    callback.onExtrasChanged(connection, mExtras);
1334                }
1335            });
1336        }
1337    }
1338
1339    /** @hide */
1340    void onConnectionEvent(final String event, final Bundle extras) {
1341        for (CallbackRecord record : mCallbackRecords) {
1342            final RemoteConnection connection = this;
1343            final Callback callback = record.getCallback();
1344            record.getHandler().post(new Runnable() {
1345                @Override
1346                public void run() {
1347                    callback.onConnectionEvent(connection, event, extras);
1348                }
1349            });
1350        }
1351    }
1352
1353    /**
1354     * Create a RemoteConnection represents a failure, and which will be in
1355     * {@link Connection#STATE_DISCONNECTED}. Attempting to use it for anything will almost
1356     * certainly result in bad things happening. Do not do this.
1357     *
1358     * @return a failed {@link RemoteConnection}
1359     *
1360     * @hide
1361     */
1362    public static RemoteConnection failure(DisconnectCause disconnectCause) {
1363        return new RemoteConnection(disconnectCause);
1364    }
1365
1366    private static final class CallbackRecord extends Callback {
1367        private final Callback mCallback;
1368        private final Handler mHandler;
1369
1370        public CallbackRecord(Callback callback, Handler handler) {
1371            mCallback = callback;
1372            mHandler = handler;
1373        }
1374
1375        public Callback getCallback() {
1376            return mCallback;
1377        }
1378
1379        public Handler getHandler() {
1380            return mHandler;
1381        }
1382    }
1383}
1384