summaryrefslogtreecommitdiffstats
path: root/telecomm/java/android/telecom/Connection.java
diff options
context:
space:
mode:
authorTyler Gunn <tgunn@google.com>2014-09-12 22:16:17 -0700
committerTyler Gunn <tgunn@google.com>2014-09-12 22:16:17 -0700
commitef9f6f957d897ea0ed82114185b8fa3fefd4917b (patch)
tree4aff42f3d54f4454e598f27829c4983ba808afa7 /telecomm/java/android/telecom/Connection.java
parent4b5c2d3cfc8aec4ab90097734a3556a0d0c0e68d (diff)
downloadframeworks_base-ef9f6f957d897ea0ed82114185b8fa3fefd4917b.zip
frameworks_base-ef9f6f957d897ea0ed82114185b8fa3fefd4917b.tar.gz
frameworks_base-ef9f6f957d897ea0ed82114185b8fa3fefd4917b.tar.bz2
Renaming Telecomm to Telecom.
- Changing package from android.telecomm to android.telecom - Changing package from com.android.telecomm to com.android.server.telecomm. - Renaming TelecommManager to TelecomManager. Bug: 17364651 Change-Id: I192cb5d189f55db012ea72ee82ccc5aedbc21638
Diffstat (limited to 'telecomm/java/android/telecom/Connection.java')
-rw-r--r--telecomm/java/android/telecom/Connection.java1129
1 files changed, 1129 insertions, 0 deletions
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
new file mode 100644
index 0000000..5f63af3
--- /dev/null
+++ b/telecomm/java/android/telecom/Connection.java
@@ -0,0 +1,1129 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import com.android.internal.telecom.IVideoCallback;
+import com.android.internal.telecom.IVideoProvider;
+
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.DisconnectCause;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Represents a connection to a remote endpoint that carries voice traffic.
+ * <p>
+ * Implementations create a custom subclass of {@code Connection} and return it to the framework
+ * as the return value of
+ * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
+ * or
+ * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * Implementations are then responsible for updating the state of the {@code Connection}, and
+ * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
+ * longer used and associated resources may be recovered.
+ */
+public abstract class Connection {
+
+ public static final int STATE_INITIALIZING = 0;
+
+ public static final int STATE_NEW = 1;
+
+ public static final int STATE_RINGING = 2;
+
+ public static final int STATE_DIALING = 3;
+
+ public static final int STATE_ACTIVE = 4;
+
+ public static final int STATE_HOLDING = 5;
+
+ public static final int STATE_DISCONNECTED = 6;
+
+ // Flag controlling whether PII is emitted into the logs
+ private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
+
+ /** @hide */
+ public abstract static class Listener {
+ public void onStateChanged(Connection c, int state) {}
+ public void onAddressChanged(Connection c, Uri newAddress, int presentation) {}
+ public void onCallerDisplayNameChanged(
+ Connection c, String callerDisplayName, int presentation) {}
+ public void onVideoStateChanged(Connection c, int videoState) {}
+ public void onDisconnected(Connection c, int cause, String message) {}
+ public void onPostDialWait(Connection c, String remaining) {}
+ public void onRingbackRequested(Connection c, boolean ringback) {}
+ public void onDestroyed(Connection c) {}
+ public void onCallCapabilitiesChanged(Connection c, int callCapabilities) {}
+ public void onVideoProviderChanged(
+ Connection c, VideoProvider videoProvider) {}
+ public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
+ public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
+ public void onConferenceableConnectionsChanged(
+ Connection c, List<Connection> conferenceableConnections) {}
+ public void onConferenceChanged(Connection c, Conference conference) {}
+ }
+
+ /** @hide */
+ public static abstract class VideoProvider {
+
+ /**
+ * Video is not being received (no protocol pause was issued).
+ */
+ public static final int SESSION_EVENT_RX_PAUSE = 1;
+
+ /**
+ * Video reception has resumed after a SESSION_EVENT_RX_PAUSE.
+ */
+ public static final int SESSION_EVENT_RX_RESUME = 2;
+
+ /**
+ * Video transmission has begun. This occurs after a negotiated start of video transmission
+ * when the underlying protocol has actually begun transmitting video to the remote party.
+ */
+ public static final int SESSION_EVENT_TX_START = 3;
+
+ /**
+ * Video transmission has stopped. This occurs after a negotiated stop of video transmission
+ * when the underlying protocol has actually stopped transmitting video to the remote party.
+ */
+ public static final int SESSION_EVENT_TX_STOP = 4;
+
+ /**
+ * A camera failure has occurred for the selected camera. The In-Call UI can use this as a
+ * cue to inform the user the camera is not available.
+ */
+ public static final int SESSION_EVENT_CAMERA_FAILURE = 5;
+
+ /**
+ * Issued after {@code SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready for
+ * operation. The In-Call UI can use this as a cue to inform the user that the camera has
+ * become available again.
+ */
+ public static final int SESSION_EVENT_CAMERA_READY = 6;
+
+ /**
+ * Session modify request was successful.
+ */
+ public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
+
+ /**
+ * Session modify request failed.
+ */
+ public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
+
+ /**
+ * Session modify request ignored due to invalid parameters.
+ */
+ public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
+
+ private static final int MSG_SET_VIDEO_CALLBACK = 1;
+ private static final int MSG_SET_CAMERA = 2;
+ private static final int MSG_SET_PREVIEW_SURFACE = 3;
+ private static final int MSG_SET_DISPLAY_SURFACE = 4;
+ private static final int MSG_SET_DEVICE_ORIENTATION = 5;
+ private static final int MSG_SET_ZOOM = 6;
+ private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
+ private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
+ private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
+ private static final int MSG_REQUEST_CALL_DATA_USAGE = 10;
+ private static final int MSG_SET_PAUSE_IMAGE = 11;
+
+ private final VideoProvider.VideoProviderHandler
+ mMessageHandler = new VideoProvider.VideoProviderHandler();
+ private final VideoProvider.VideoProviderBinder mBinder;
+ private IVideoCallback mVideoCallback;
+
+ /**
+ * Default handler used to consolidate binder method calls onto a single thread.
+ */
+ private final class VideoProviderHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_VIDEO_CALLBACK:
+ mVideoCallback = IVideoCallback.Stub.asInterface((IBinder) msg.obj);
+ break;
+ case MSG_SET_CAMERA:
+ onSetCamera((String) msg.obj);
+ break;
+ case MSG_SET_PREVIEW_SURFACE:
+ onSetPreviewSurface((Surface) msg.obj);
+ break;
+ case MSG_SET_DISPLAY_SURFACE:
+ onSetDisplaySurface((Surface) msg.obj);
+ break;
+ case MSG_SET_DEVICE_ORIENTATION:
+ onSetDeviceOrientation(msg.arg1);
+ break;
+ case MSG_SET_ZOOM:
+ onSetZoom((Float) msg.obj);
+ break;
+ case MSG_SEND_SESSION_MODIFY_REQUEST:
+ onSendSessionModifyRequest((VideoProfile) msg.obj);
+ break;
+ case MSG_SEND_SESSION_MODIFY_RESPONSE:
+ onSendSessionModifyResponse((VideoProfile) msg.obj);
+ break;
+ case MSG_REQUEST_CAMERA_CAPABILITIES:
+ onRequestCameraCapabilities();
+ break;
+ case MSG_REQUEST_CALL_DATA_USAGE:
+ onRequestCallDataUsage();
+ break;
+ case MSG_SET_PAUSE_IMAGE:
+ onSetPauseImage((String) msg.obj);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * IVideoProvider stub implementation.
+ */
+ private final class VideoProviderBinder extends IVideoProvider.Stub {
+ public void setVideoCallback(IBinder videoCallbackBinder) {
+ mMessageHandler.obtainMessage(
+ MSG_SET_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
+ }
+
+ public void setCamera(String cameraId) {
+ mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
+ }
+
+ public void setPreviewSurface(Surface surface) {
+ mMessageHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
+ }
+
+ public void setDisplaySurface(Surface surface) {
+ mMessageHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
+ }
+
+ public void setDeviceOrientation(int rotation) {
+ mMessageHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation).sendToTarget();
+ }
+
+ public void setZoom(float value) {
+ mMessageHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
+ }
+
+ public void sendSessionModifyRequest(VideoProfile requestProfile) {
+ mMessageHandler.obtainMessage(
+ MSG_SEND_SESSION_MODIFY_REQUEST, requestProfile).sendToTarget();
+ }
+
+ public void sendSessionModifyResponse(VideoProfile responseProfile) {
+ mMessageHandler.obtainMessage(
+ MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
+ }
+
+ public void requestCameraCapabilities() {
+ mMessageHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
+ }
+
+ public void requestCallDataUsage() {
+ mMessageHandler.obtainMessage(MSG_REQUEST_CALL_DATA_USAGE).sendToTarget();
+ }
+
+ public void setPauseImage(String uri) {
+ mMessageHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
+ }
+ }
+
+ public VideoProvider() {
+ mBinder = new VideoProvider.VideoProviderBinder();
+ }
+
+ /**
+ * Returns binder object which can be used across IPC methods.
+ * @hide
+ */
+ public final IVideoProvider getInterface() {
+ return mBinder;
+ }
+
+ /**
+ * Sets the camera to be used for video recording in a video call.
+ *
+ * @param cameraId The id of the camera.
+ */
+ public abstract void onSetCamera(String cameraId);
+
+ /**
+ * Sets the surface to be used for displaying a preview of what the user's camera is
+ * currently capturing. When video transmission is enabled, this is the video signal which
+ * is sent to the remote device.
+ *
+ * @param surface The surface.
+ */
+ public abstract void onSetPreviewSurface(Surface surface);
+
+ /**
+ * Sets the surface to be used for displaying the video received from the remote device.
+ *
+ * @param surface The surface.
+ */
+ public abstract void onSetDisplaySurface(Surface surface);
+
+ /**
+ * Sets the device orientation, in degrees. Assumes that a standard portrait orientation of
+ * the device is 0 degrees.
+ *
+ * @param rotation The device orientation, in degrees.
+ */
+ public abstract void onSetDeviceOrientation(int rotation);
+
+ /**
+ * Sets camera zoom ratio.
+ *
+ * @param value The camera zoom ratio.
+ */
+ public abstract void onSetZoom(float value);
+
+ /**
+ * Issues a request to modify the properties of the current session. The request is
+ * sent to the remote device where it it handled by the In-Call UI.
+ * Some examples of session modification requests: upgrade call from audio to video,
+ * downgrade call from video to audio, pause video.
+ *
+ * @param requestProfile The requested call video properties.
+ */
+ public abstract void onSendSessionModifyRequest(VideoProfile requestProfile);
+
+ /**te
+ * Provides a response to a request to change the current call session video
+ * properties.
+ * This is in response to a request the InCall UI has received via the InCall UI.
+ *
+ * @param responseProfile The response call video properties.
+ */
+ public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
+
+ /**
+ * Issues a request to the video provider to retrieve the camera capabilities.
+ * Camera capabilities are reported back to the caller via the In-Call UI.
+ */
+ public abstract void onRequestCameraCapabilities();
+
+ /**
+ * Issues a request to the video telephony framework to retrieve the cumulative data usage
+ * for the current call. Data usage is reported back to the caller via the
+ * InCall UI.
+ */
+ public abstract void onRequestCallDataUsage();
+
+ /**
+ * Provides the video telephony framework with the URI of an image to be displayed to remote
+ * devices when the video signal is paused.
+ *
+ * @param uri URI of image to display.
+ */
+ public abstract void onSetPauseImage(String uri);
+
+ /**
+ * Invokes callback method defined in In-Call UI.
+ *
+ * @param videoProfile The requested video call profile.
+ */
+ public void receiveSessionModifyRequest(VideoProfile videoProfile) {
+ if (mVideoCallback != null) {
+ try {
+ mVideoCallback.receiveSessionModifyRequest(videoProfile);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Invokes callback method defined in In-Call UI.
+ *
+ * @param status Status of the session modify request. Valid values are
+ * {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
+ * {@link VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
+ * {@link VideoProvider#SESSION_MODIFY_REQUEST_INVALID}
+ * @param requestedProfile The original request which was sent to the remote device.
+ * @param responseProfile The actual profile changes made by the remote device.
+ */
+ public void receiveSessionModifyResponse(int status,
+ VideoProfile requestedProfile, VideoProfile responseProfile) {
+ if (mVideoCallback != null) {
+ try {
+ mVideoCallback.receiveSessionModifyResponse(
+ status, requestedProfile, responseProfile);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Invokes callback method defined in In-Call UI.
+ *
+ * Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
+ * {@link VideoProvider#SESSION_EVENT_RX_RESUME},
+ * {@link VideoProvider#SESSION_EVENT_TX_START},
+ * {@link VideoProvider#SESSION_EVENT_TX_STOP}
+ *
+ * @param event The event.
+ */
+ public void handleCallSessionEvent(int event) {
+ if (mVideoCallback != null) {
+ try {
+ mVideoCallback.handleCallSessionEvent(event);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Invokes callback method defined in In-Call UI.
+ *
+ * @param width The updated peer video width.
+ * @param height The updated peer video height.
+ */
+ public void changePeerDimensions(int width, int height) {
+ if (mVideoCallback != null) {
+ try {
+ mVideoCallback.changePeerDimensions(width, height);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Invokes callback method defined in In-Call UI.
+ *
+ * @param dataUsage The updated data usage.
+ */
+ public void changeCallDataUsage(int dataUsage) {
+ if (mVideoCallback != null) {
+ try {
+ mVideoCallback.changeCallDataUsage(dataUsage);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Invokes callback method defined in In-Call UI.
+ *
+ * @param cameraCapabilities The changed camera capabilities.
+ */
+ public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
+ if (mVideoCallback != null) {
+ try {
+ mVideoCallback.changeCameraCapabilities(cameraCapabilities);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+ }
+
+ private final Listener mConnectionDeathListener = new Listener() {
+ @Override
+ public void onDestroyed(Connection c) {
+ if (mConferenceableConnections.remove(c)) {
+ fireOnConferenceableConnectionsChanged();
+ }
+ }
+ };
+
+ /**
+ * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
+ * load factor before resizing, 1 means we only expect a single thread to
+ * access the map so make only a single shard
+ */
+ private final Set<Listener> mListeners = Collections.newSetFromMap(
+ new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
+ private final List<Connection> mConferenceableConnections = new ArrayList<>();
+ private final List<Connection> mUnmodifiableConferenceableConnections =
+ Collections.unmodifiableList(mConferenceableConnections);
+
+ private int mState = STATE_NEW;
+ private AudioState mAudioState;
+ private Uri mAddress;
+ private int mAddressPresentation;
+ private String mCallerDisplayName;
+ private int mCallerDisplayNamePresentation;
+ private boolean mRingbackRequested = false;
+ private int mCallCapabilities;
+ private VideoProvider mVideoProvider;
+ private boolean mAudioModeIsVoip;
+ private StatusHints mStatusHints;
+ private int mVideoState;
+ private int mDisconnectCause;
+ private String mDisconnectMessage;
+ private Conference mConference;
+ private ConnectionService mConnectionService;
+
+ /**
+ * Create a new Connection.
+ */
+ public Connection() {}
+
+ /**
+ * @return The address (e.g., phone number) to which this Connection is currently communicating.
+ */
+ public final Uri getAddress() {
+ return mAddress;
+ }
+
+ /**
+ * @return The presentation requirements for the address.
+ * See {@link TelecomManager} for valid values.
+ */
+ public final int getAddressPresentation() {
+ return mAddressPresentation;
+ }
+
+ /**
+ * @return The caller display name (CNAP).
+ */
+ public final String getCallerDisplayName() {
+ return mCallerDisplayName;
+ }
+
+ /**
+ * @return The presentation requirements for the handle.
+ * See {@link TelecomManager} for valid values.
+ */
+ public final int getCallerDisplayNamePresentation() {
+ return mCallerDisplayNamePresentation;
+ }
+
+ /**
+ * @return The state of this Connection.
+ */
+ public final int getState() {
+ return mState;
+ }
+
+ /**
+ * Returns the video state of the call.
+ * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
+ * {@link VideoProfile.VideoState#BIDIRECTIONAL},
+ * {@link VideoProfile.VideoState#TX_ENABLED},
+ * {@link VideoProfile.VideoState#RX_ENABLED}.
+ *
+ * @return The video state of the call.
+ * @hide
+ */
+ public final int getVideoState() {
+ return mVideoState;
+ }
+
+ /**
+ * @return The audio state of the call, describing how its audio is currently
+ * being routed by the system. This is {@code null} if this Connection
+ * does not directly know about its audio state.
+ */
+ public final AudioState getAudioState() {
+ return mAudioState;
+ }
+
+ /**
+ * @return The conference that this connection is a part of. Null if it is not part of any
+ * conference.
+ */
+ public final Conference getConference() {
+ return mConference;
+ }
+
+ /**
+ * Returns whether this connection is requesting that the system play a ringback tone
+ * on its behalf.
+ */
+ public final boolean isRingbackRequested() {
+ return mRingbackRequested;
+ }
+
+ /**
+ * @return True if the connection's audio mode is VOIP.
+ */
+ public final boolean getAudioModeIsVoip() {
+ return mAudioModeIsVoip;
+ }
+
+ /**
+ * @return The status hints for this connection.
+ */
+ public final StatusHints getStatusHints() {
+ return mStatusHints;
+ }
+
+ /**
+ * Assign a listener to be notified of state changes.
+ *
+ * @param l A listener.
+ * @return This Connection.
+ *
+ * @hide
+ */
+ public final Connection addConnectionListener(Listener l) {
+ mListeners.add(l);
+ return this;
+ }
+
+ /**
+ * Remove a previously assigned listener that was being notified of state changes.
+ *
+ * @param l A Listener.
+ * @return This Connection.
+ *
+ * @hide
+ */
+ public final Connection removeConnectionListener(Listener l) {
+ if (l != null) {
+ mListeners.remove(l);
+ }
+ return this;
+ }
+
+ /**
+ * @return The {@link DisconnectCause} for this connection.
+ */
+ public final int getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ /**
+ * @return The disconnect message for this connection.
+ */
+ public final String getDisconnectMessage() {
+ return mDisconnectMessage;
+ }
+
+ /**
+ * Inform this Connection that the state of its audio output has been changed externally.
+ *
+ * @param state The new audio state.
+ * @hide
+ */
+ final void setAudioState(AudioState state) {
+ Log.d(this, "setAudioState %s", state);
+ mAudioState = state;
+ onAudioStateChanged(state);
+ }
+
+ /**
+ * @param state An integer value of a {@code STATE_*} constant.
+ * @return A string representation of the value.
+ */
+ public static String stateToString(int state) {
+ switch (state) {
+ case STATE_INITIALIZING:
+ return "STATE_INITIALIZING";
+ case STATE_NEW:
+ return "STATE_NEW";
+ case STATE_RINGING:
+ return "STATE_RINGING";
+ case STATE_DIALING:
+ return "STATE_DIALING";
+ case STATE_ACTIVE:
+ return "STATE_ACTIVE";
+ case STATE_HOLDING:
+ return "STATE_HOLDING";
+ case STATE_DISCONNECTED:
+ return "DISCONNECTED";
+ default:
+ Log.wtf(Connection.class, "Unknown state %d", state);
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * Returns the connection's {@link PhoneCapabilities}
+ */
+ public final int getCallCapabilities() {
+ return mCallCapabilities;
+ }
+
+ /**
+ * Sets the value of the {@link #getAddress()} property.
+ *
+ * @param address The new address.
+ * @param presentation The presentation requirements for the address.
+ * See {@link TelecomManager} for valid values.
+ */
+ public final void setAddress(Uri address, int presentation) {
+ Log.d(this, "setAddress %s", address);
+ mAddress = address;
+ mAddressPresentation = presentation;
+ for (Listener l : mListeners) {
+ l.onAddressChanged(this, address, presentation);
+ }
+ }
+
+ /**
+ * Sets the caller display name (CNAP).
+ *
+ * @param callerDisplayName The new display name.
+ * @param presentation The presentation requirements for the handle.
+ * See {@link TelecomManager} for valid values.
+ */
+ public final void setCallerDisplayName(String callerDisplayName, int presentation) {
+ Log.d(this, "setCallerDisplayName %s", callerDisplayName);
+ mCallerDisplayName = callerDisplayName;
+ mCallerDisplayNamePresentation = presentation;
+ for (Listener l : mListeners) {
+ l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
+ }
+ }
+
+ /**
+ * Set the video state for the connection.
+ * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
+ * {@link VideoProfile.VideoState#BIDIRECTIONAL},
+ * {@link VideoProfile.VideoState#TX_ENABLED},
+ * {@link VideoProfile.VideoState#RX_ENABLED}.
+ *
+ * @param videoState The new video state.
+ * @hide
+ */
+ public final void setVideoState(int videoState) {
+ Log.d(this, "setVideoState %d", videoState);
+ mVideoState = videoState;
+ for (Listener l : mListeners) {
+ l.onVideoStateChanged(this, mVideoState);
+ }
+ }
+
+ /**
+ * Sets state to active (e.g., an ongoing call where two or more parties can actively
+ * communicate).
+ */
+ public final void setActive() {
+ setRingbackRequested(false);
+ setState(STATE_ACTIVE);
+ }
+
+ /**
+ * Sets state to ringing (e.g., an inbound ringing call).
+ */
+ public final void setRinging() {
+ setState(STATE_RINGING);
+ }
+
+ /**
+ * Sets state to initializing (this Connection is not yet ready to be used).
+ */
+ public final void setInitializing() {
+ setState(STATE_INITIALIZING);
+ }
+
+ /**
+ * Sets state to initialized (the Connection has been set up and is now ready to be used).
+ */
+ public final void setInitialized() {
+ setState(STATE_NEW);
+ }
+
+ /**
+ * Sets state to dialing (e.g., dialing an outbound call).
+ */
+ public final void setDialing() {
+ setState(STATE_DIALING);
+ }
+
+ /**
+ * Sets state to be on hold.
+ */
+ public final void setOnHold() {
+ setState(STATE_HOLDING);
+ }
+
+ /**
+ * Sets the video call provider.
+ * @param videoProvider The video provider.
+ * @hide
+ */
+ public final void setVideoProvider(VideoProvider videoProvider) {
+ mVideoProvider = videoProvider;
+ for (Listener l : mListeners) {
+ l.onVideoProviderChanged(this, videoProvider);
+ }
+ }
+
+ /** @hide */
+ public final VideoProvider getVideoProvider() {
+ return mVideoProvider;
+ }
+
+ /**
+ * Sets state to disconnected.
+ *
+ * @param cause The reason for the disconnection, any of
+ * {@link DisconnectCause}.
+ * @param message Optional call-service-provided message about the disconnect.
+ */
+ public final void setDisconnected(int cause, String message) {
+ mDisconnectCause = cause;
+ mDisconnectMessage = message;
+ setState(STATE_DISCONNECTED);
+ Log.d(this, "Disconnected with cause %d message %s", cause, message);
+ for (Listener l : mListeners) {
+ l.onDisconnected(this, cause, message);
+ }
+ }
+
+ /**
+ * TODO: Needs documentation.
+ */
+ public final void setPostDialWait(String remaining) {
+ for (Listener l : mListeners) {
+ l.onPostDialWait(this, remaining);
+ }
+ }
+
+ /**
+ * Requests that the framework play a ringback tone. This is to be invoked by implementations
+ * that do not play a ringback tone themselves in the call's audio stream.
+ *
+ * @param ringback Whether the ringback tone is to be played.
+ */
+ public final void setRingbackRequested(boolean ringback) {
+ if (mRingbackRequested != ringback) {
+ mRingbackRequested = ringback;
+ for (Listener l : mListeners) {
+ l.onRingbackRequested(this, ringback);
+ }
+ }
+ }
+
+ /**
+ * Sets the connection's {@link PhoneCapabilities}.
+ *
+ * @param callCapabilities The new call capabilities.
+ */
+ public final void setCallCapabilities(int callCapabilities) {
+ if (mCallCapabilities != callCapabilities) {
+ mCallCapabilities = callCapabilities;
+ for (Listener l : mListeners) {
+ l.onCallCapabilitiesChanged(this, mCallCapabilities);
+ }
+ }
+ }
+
+ /**
+ * Tears down the Connection object.
+ */
+ public final void destroy() {
+ for (Listener l : mListeners) {
+ l.onDestroyed(this);
+ }
+ }
+
+ /**
+ * Requests that the framework use VOIP audio mode for this connection.
+ *
+ * @param isVoip True if the audio mode is VOIP.
+ */
+ public final void setAudioModeIsVoip(boolean isVoip) {
+ mAudioModeIsVoip = isVoip;
+ for (Listener l : mListeners) {
+ l.onAudioModeIsVoipChanged(this, isVoip);
+ }
+ }
+
+ /**
+ * Sets the label and icon status to display in the in-call UI.
+ *
+ * @param statusHints The status label and icon to set.
+ */
+ public final void setStatusHints(StatusHints statusHints) {
+ mStatusHints = statusHints;
+ for (Listener l : mListeners) {
+ l.onStatusHintsChanged(this, statusHints);
+ }
+ }
+
+ /**
+ * Sets the connections with which this connection can be conferenced.
+ *
+ * @param conferenceableConnections The set of connections this connection can conference with.
+ */
+ public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
+ clearConferenceableList();
+ for (Connection c : conferenceableConnections) {
+ // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
+ // small amount of items here.
+ if (!mConferenceableConnections.contains(c)) {
+ c.addConnectionListener(mConnectionDeathListener);
+ mConferenceableConnections.add(c);
+ }
+ }
+ fireOnConferenceableConnectionsChanged();
+ }
+
+ /**
+ * Obtains the connections with which this connection can be conferenced.
+ */
+ public final List<Connection> getConferenceableConnections() {
+ return mUnmodifiableConferenceableConnections;
+ }
+
+ /*
+ * @hide
+ */
+ public final void setConnectionService(ConnectionService connectionService) {
+ if (mConnectionService != null) {
+ Log.e(this, new Exception(), "Trying to set ConnectionService on a connection " +
+ "which is already associated with another ConnectionService.");
+ } else {
+ mConnectionService = connectionService;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public final void unsetConnectionService(ConnectionService connectionService) {
+ if (mConnectionService != connectionService) {
+ Log.e(this, new Exception(), "Trying to remove ConnectionService from a Connection " +
+ "that does not belong to the ConnectionService.");
+ } else {
+ mConnectionService = null;
+ }
+ }
+
+ /**
+ * Sets the conference that this connection is a part of. This will fail if the connection is
+ * already part of a conference call. {@link #resetConference} to un-set the conference first.
+ *
+ * @param conference The conference.
+ * @return {@code true} if the conference was successfully set.
+ * @hide
+ */
+ public final boolean setConference(Conference conference) {
+ // We check to see if it is already part of another conference.
+ if (mConference == null) {
+ mConference = conference;
+ if (mConnectionService != null && mConnectionService.containsConference(conference)) {
+ fireConferenceChanged();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Resets the conference that this connection is a part of.
+ * @hide
+ */
+ public final void resetConference() {
+ if (mConference != null) {
+ Log.d(this, "Conference reset");
+ mConference = null;
+ fireConferenceChanged();
+ }
+ }
+
+ /**
+ * Notifies this Connection that the {@link #getAudioState()} property has a new value.
+ *
+ * @param state The new call audio state.
+ */
+ public void onAudioStateChanged(AudioState state) {}
+
+ /**
+ * Notifies this Connection of an internal state change. This method is called after the
+ * state is changed.
+ *
+ * @param state The new state, one of the {@code STATE_*} constants.
+ */
+ public void onStateChanged(int state) {}
+
+ /**
+ * Notifies this Connection of a request to play a DTMF tone.
+ *
+ * @param c A DTMF character.
+ */
+ public void onPlayDtmfTone(char c) {}
+
+ /**
+ * Notifies this Connection of a request to stop any currently playing DTMF tones.
+ */
+ public void onStopDtmfTone() {}
+
+ /**
+ * Notifies this Connection of a request to disconnect.
+ */
+ public void onDisconnect() {}
+
+ /**
+ * Notifies this Connection of a request to separate from its parent conference.
+ */
+ public void onSeparate() {}
+
+ /**
+ * Notifies this Connection of a request to abort.
+ */
+ public void onAbort() {}
+
+ /**
+ * Notifies this Connection of a request to hold.
+ */
+ public void onHold() {}
+
+ /**
+ * Notifies this Connection of a request to exit a hold state.
+ */
+ public void onUnhold() {}
+
+ /**
+ * Notifies this Connection, which is in {@link #STATE_RINGING}, of
+ * a request to accept.
+ *
+ * @param videoState The video state in which to answer the call.
+ * @hide
+ */
+ public void onAnswer(int videoState) {}
+
+ /**
+ * Notifies this Connection, which is in {@link #STATE_RINGING}, of
+ * a request to accept.
+ */
+ public void onAnswer() {
+ onAnswer(VideoProfile.VideoState.AUDIO_ONLY);
+ }
+
+ /**
+ * Notifies this Connection, which is in {@link #STATE_RINGING}, of
+ * a request to reject.
+ */
+ public void onReject() {}
+
+ /**
+ * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
+ */
+ public void onPostDialContinue(boolean proceed) {}
+
+ /**
+ * Merge this connection and the specified connection into a conference call. Once the
+ * connections are merged, the calls should be added to the an existing or new
+ * {@code Conference} instance. For new {@code Conference} instances, use
+ * {@code ConnectionService#addConference}.
+ *
+ * @param otherConnection The connection with which this connection should be conferenced.
+ */
+ public void onConferenceWith(Connection otherConnection) {}
+
+ static String toLogSafePhoneNumber(String number) {
+ // For unknown number, log empty string.
+ if (number == null) {
+ return "";
+ }
+
+ if (PII_DEBUG) {
+ // When PII_DEBUG is true we emit PII.
+ return number;
+ }
+
+ // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
+ // sanitized phone numbers.
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number.length(); i++) {
+ char c = number.charAt(i);
+ if (c == '-' || c == '@' || c == '.') {
+ builder.append(c);
+ } else {
+ builder.append('x');
+ }
+ }
+ return builder.toString();
+ }
+
+ private void setState(int state) {
+ if (mState == STATE_DISCONNECTED && mState != state) {
+ Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
+ return;
+ }
+ if (mState != state) {
+ Log.d(this, "setState: %s", stateToString(state));
+ mState = state;
+ onStateChanged(state);
+ for (Listener l : mListeners) {
+ l.onStateChanged(this, state);
+ }
+ }
+ }
+
+ private static class FailureSignalingConnection extends Connection {
+ public FailureSignalingConnection(int cause, String message) {
+ setDisconnected(cause, message);
+ }
+ }
+
+ /**
+ * Return a {@code Connection} which represents a failed connection attempt. The returned
+ * {@code Connection} will have a {@link #getDisconnectCause()} and
+ * {@link #getDisconnectMessage()} as specified, and a {@link #getState()} of
+ * {@link #STATE_DISCONNECTED}.
+ * <p>
+ * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
+ * so users of this method need not maintain a reference to its return value to destroy it.
+ *
+ * @param cause The disconnect cause, ({@see DisconnectCause}).
+ * @param message A reason for why the connection failed (not intended to be shown to the user).
+ * @return A {@code Connection} which indicates failure.
+ */
+ public static Connection createFailedConnection(int cause, String message) {
+ return new FailureSignalingConnection(cause, message);
+ }
+
+ /**
+ * Return a {@code Connection} which represents a canceled connection attempt. The returned
+ * {@code Connection} will have state {@link #STATE_DISCONNECTED}, and cannot be moved out of
+ * that state. This connection should not be used for anything, and no other
+ * {@code Connection}s should be attempted.
+ * <p>
+ * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
+ * so users of this method need not maintain a reference to its return value to destroy it.
+ *
+ * @return A {@code Connection} which indicates that the underlying call should be canceled.
+ */
+ public static Connection createCanceledConnection() {
+ return new FailureSignalingConnection(DisconnectCause.OUTGOING_CANCELED, null);
+ }
+
+ private final void fireOnConferenceableConnectionsChanged() {
+ for (Listener l : mListeners) {
+ l.onConferenceableConnectionsChanged(this, mConferenceableConnections);
+ }
+ }
+
+ private final void fireConferenceChanged() {
+ for (Listener l : mListeners) {
+ l.onConferenceChanged(this, mConference);
+ }
+ }
+
+ private final void clearConferenceableList() {
+ for (Connection c : mConferenceableConnections) {
+ c.removeConnectionListener(mConnectionDeathListener);
+ }
+ mConferenceableConnections.clear();
+ }
+}