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