summaryrefslogtreecommitdiffstats
path: root/telecomm
diff options
context:
space:
mode:
Diffstat (limited to 'telecomm')
-rw-r--r--telecomm/java/android/telecom/AudioState.aidl22
-rw-r--r--telecomm/java/android/telecom/AudioState.java161
-rw-r--r--telecomm/java/android/telecom/Call.java827
-rw-r--r--telecomm/java/android/telecom/CallProperties.java26
-rw-r--r--telecomm/java/android/telecom/CallState.java127
-rw-r--r--telecomm/java/android/telecom/CameraCapabilities.aidl22
-rw-r--r--telecomm/java/android/telecom/CameraCapabilities.java144
-rw-r--r--telecomm/java/android/telecom/Conference.java312
-rw-r--r--telecomm/java/android/telecom/Connection.java1116
-rw-r--r--telecomm/java/android/telecom/ConnectionRequest.aidl22
-rw-r--r--telecomm/java/android/telecom/ConnectionRequest.java140
-rw-r--r--telecomm/java/android/telecom/ConnectionService.java984
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapter.java347
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapterServant.java357
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.aidl22
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java249
-rw-r--r--telecomm/java/android/telecom/GatewayInfo.aidl22
-rw-r--r--telecomm/java/android/telecom/GatewayInfo.java108
-rw-r--r--telecomm/java/android/telecom/InCallAdapter.java274
-rw-r--r--telecomm/java/android/telecom/InCallService.java365
-rw-r--r--telecomm/java/android/telecom/Log.java181
-rw-r--r--telecomm/java/android/telecom/ParcelableCall.aidl22
-rw-r--r--telecomm/java/android/telecom/ParcelableCall.java330
-rw-r--r--telecomm/java/android/telecom/ParcelableConference.aidl19
-rw-r--r--telecomm/java/android/telecom/ParcelableConference.java111
-rw-r--r--telecomm/java/android/telecom/ParcelableConnection.aidl22
-rw-r--r--telecomm/java/android/telecom/ParcelableConnection.java222
-rw-r--r--telecomm/java/android/telecom/Phone.java284
-rw-r--r--telecomm/java/android/telecom/PhoneAccount.aidl22
-rw-r--r--telecomm/java/android/telecom/PhoneAccount.java449
-rw-r--r--telecomm/java/android/telecom/PhoneAccountHandle.aidl22
-rw-r--r--telecomm/java/android/telecom/PhoneAccountHandle.java120
-rw-r--r--telecomm/java/android/telecom/PhoneCapabilities.java139
-rw-r--r--telecomm/java/android/telecom/RemoteConference.java212
-rw-r--r--telecomm/java/android/telecom/RemoteConnection.java898
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionManager.java88
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java383
-rw-r--r--telecomm/java/android/telecom/Response.java40
-rw-r--r--telecomm/java/android/telecom/StatusHints.aidl22
-rw-r--r--telecomm/java/android/telecom/StatusHints.java150
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java858
-rw-r--r--telecomm/java/android/telecom/VideoCallImpl.java247
-rw-r--r--telecomm/java/android/telecom/VideoCallbackServant.java160
-rw-r--r--telecomm/java/android/telecom/VideoProfile.aidl23
-rw-r--r--telecomm/java/android/telecom/VideoProfile.java231
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionService.aidl73
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl80
-rw-r--r--telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl62
-rw-r--r--telecomm/java/com/android/internal/telecom/IInCallService.aidl46
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl175
-rw-r--r--telecomm/java/com/android/internal/telecom/IVideoCallback.aidl45
-rw-r--r--telecomm/java/com/android/internal/telecom/IVideoProvider.aidl49
-rw-r--r--telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl29
53 files changed, 11461 insertions, 0 deletions
diff --git a/telecomm/java/android/telecom/AudioState.aidl b/telecomm/java/android/telecom/AudioState.aidl
new file mode 100644
index 0000000..b36e238
--- /dev/null
+++ b/telecomm/java/android/telecom/AudioState.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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;
+
+/**
+ * {@hide}
+ */
+parcelable AudioState;
diff --git a/telecomm/java/android/telecom/AudioState.java b/telecomm/java/android/telecom/AudioState.java
new file mode 100644
index 0000000..d0e2860
--- /dev/null
+++ b/telecomm/java/android/telecom/AudioState.java
@@ -0,0 +1,161 @@
+/*
+ * 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 android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Locale;
+
+/**
+ * Encapsulates all audio states during a call.
+ */
+public final class AudioState implements Parcelable {
+ /** Direct the audio stream through the device's earpiece. */
+ public static final int ROUTE_EARPIECE = 0x00000001;
+
+ /** Direct the audio stream through Bluetooth. */
+ public static final int ROUTE_BLUETOOTH = 0x00000002;
+
+ /** Direct the audio stream through a wired headset. */
+ public static final int ROUTE_WIRED_HEADSET = 0x00000004;
+
+ /** Direct the audio stream through the device's speakerphone. */
+ public static final int ROUTE_SPEAKER = 0x00000008;
+
+ /**
+ * Direct the audio stream through the device's earpiece or wired headset if one is
+ * connected.
+ */
+ public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
+
+ /** Bit mask of all possible audio routes.
+ *
+ * @hide
+ */
+ public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
+ ROUTE_SPEAKER;
+
+ /** True if the call is muted, false otherwise. */
+ public final boolean isMuted;
+
+ /** The route to use for the audio stream. */
+ public final int route;
+
+ /** Bit vector of all routes supported by this call. */
+ public final int supportedRouteMask;
+
+ public AudioState(boolean isMuted, int route, int supportedRouteMask) {
+ this.isMuted = isMuted;
+ this.route = route;
+ this.supportedRouteMask = supportedRouteMask;
+ }
+
+ public AudioState(AudioState state) {
+ isMuted = state.isMuted;
+ route = state.route;
+ supportedRouteMask = state.supportedRouteMask;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof AudioState)) {
+ return false;
+ }
+ AudioState state = (AudioState) obj;
+ return isMuted == state.isMuted && route == state.route &&
+ supportedRouteMask == state.supportedRouteMask;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.US,
+ "[AudioState isMuted: %b, route; %s, supportedRouteMask: %s]",
+ isMuted, audioRouteToString(route), audioRouteToString(supportedRouteMask));
+ }
+
+ /** @hide */
+ public static String audioRouteToString(int route) {
+ if (route == 0 || (route & ~ROUTE_ALL) != 0x0) {
+ return "UNKNOWN";
+ }
+
+ StringBuffer buffer = new StringBuffer();
+ if ((route & ROUTE_EARPIECE) == ROUTE_EARPIECE) {
+ listAppend(buffer, "EARPIECE");
+ }
+ if ((route & ROUTE_BLUETOOTH) == ROUTE_BLUETOOTH) {
+ listAppend(buffer, "BLUETOOTH");
+ }
+ if ((route & ROUTE_WIRED_HEADSET) == ROUTE_WIRED_HEADSET) {
+ listAppend(buffer, "WIRED_HEADSET");
+ }
+ if ((route & ROUTE_SPEAKER) == ROUTE_SPEAKER) {
+ listAppend(buffer, "SPEAKER");
+ }
+
+ return buffer.toString();
+ }
+
+ private static void listAppend(StringBuffer buffer, String str) {
+ if (buffer.length() > 0) {
+ buffer.append(", ");
+ }
+ buffer.append(str);
+ }
+
+ /**
+ * Responsible for creating AudioState objects for deserialized Parcels.
+ */
+ public static final Parcelable.Creator<AudioState> CREATOR =
+ new Parcelable.Creator<AudioState> () {
+
+ @Override
+ public AudioState createFromParcel(Parcel source) {
+ boolean isMuted = source.readByte() == 0 ? false : true;
+ int route = source.readInt();
+ int supportedRouteMask = source.readInt();
+ return new AudioState(isMuted, route, supportedRouteMask);
+ }
+
+ @Override
+ public AudioState[] newArray(int size) {
+ return new AudioState[size];
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes AudioState object into a serializeable Parcel.
+ */
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeByte((byte) (isMuted ? 1 : 0));
+ destination.writeInt(route);
+ destination.writeInt(supportedRouteMask);
+ }
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
new file mode 100644
index 0000000..1a6c52f
--- /dev/null
+++ b/telecomm/java/android/telecom/Call.java
@@ -0,0 +1,827 @@
+/*
+ * 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 android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Bundle;
+
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Represents an ongoing phone call that the in-call app should present to the user.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class Call {
+ /**
+ * The state of a {@code Call} when newly created.
+ */
+ public static final int STATE_NEW = 0;
+
+ /**
+ * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected.
+ */
+ public static final int STATE_DIALING = 1;
+
+ /**
+ * The state of an incoming {@code Call} when ringing locally, but not yet connected.
+ */
+ public static final int STATE_RINGING = 2;
+
+ /**
+ * The state of a {@code Call} when in a holding state.
+ */
+ public static final int STATE_HOLDING = 3;
+
+ /**
+ * The state of a {@code Call} when actively supporting conversation.
+ */
+ public static final int STATE_ACTIVE = 4;
+
+ /**
+ * The state of a {@code Call} when no further voice or other communication is being
+ * transmitted, the remote side has been or will inevitably be informed that the {@code Call}
+ * is no longer active, and the local data transport has or inevitably will release resources
+ * associated with this {@code Call}.
+ */
+ public static final int STATE_DISCONNECTED = 7;
+
+ /**
+ * The state of an outgoing {@code Call}, but waiting for user input before proceeding.
+ */
+ public static final int STATE_PRE_DIAL_WAIT = 8;
+
+ /**
+ * The initial state of an outgoing {@code Call}.
+ * Common transitions are to {@link #STATE_DIALING} state for a successful call or
+ * {@link #STATE_DISCONNECTED} if it failed.
+ */
+ public static final int STATE_CONNECTING = 9;
+
+ public static class Details {
+ private final Uri mHandle;
+ private final int mHandlePresentation;
+ private final String mCallerDisplayName;
+ private final int mCallerDisplayNamePresentation;
+ private final PhoneAccountHandle mAccountHandle;
+ private final int mCallCapabilities;
+ private final int mCallProperties;
+ private final DisconnectCause mDisconnectCause;
+ private final long mConnectTimeMillis;
+ private final GatewayInfo mGatewayInfo;
+ private final int mVideoState;
+ private final StatusHints mStatusHints;
+ private final Bundle mExtras;
+
+ /**
+ * @return The handle (e.g., phone number) to which the {@code Call} is currently
+ * connected.
+ */
+ public Uri getHandle() {
+ return mHandle;
+ }
+
+ /**
+ * @return The presentation requirements for the handle. See
+ * {@link TelecomManager} for valid values.
+ */
+ public int getHandlePresentation() {
+ return mHandlePresentation;
+ }
+
+ /**
+ * @return The display name for the caller.
+ */
+ public String getCallerDisplayName() {
+ return mCallerDisplayName;
+ }
+
+ /**
+ * @return The presentation requirements for the caller display name. See
+ * {@link TelecomManager} for valid values.
+ */
+ public int getCallerDisplayNamePresentation() {
+ return mCallerDisplayNamePresentation;
+ }
+
+ /**
+ * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being
+ * routed.
+ */
+ public PhoneAccountHandle getAccountHandle() {
+ return mAccountHandle;
+ }
+
+ /**
+ * @return A bitmask of the capabilities of the {@code Call}, as defined in
+ * {@link PhoneCapabilities}.
+ */
+ public int getCallCapabilities() {
+ return mCallCapabilities;
+ }
+
+ /**
+ * @return A bitmask of the properties of the {@code Call}, as defined in
+ * {@link CallProperties}.
+ */
+ public int getCallProperties() {
+ return mCallProperties;
+ }
+
+ /**
+ * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
+ * by {@link android.telecomm.DisconnectCause}.
+ */
+ public DisconnectCause getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ /**
+ * @return The time the {@code Call} has been connected. This information is updated
+ * periodically, but user interfaces should not rely on this to display any "call time
+ * clock".
+ */
+ public long getConnectTimeMillis() {
+ return mConnectTimeMillis;
+ }
+
+ /**
+ * @return Information about any calling gateway the {@code Call} may be using.
+ */
+ public GatewayInfo getGatewayInfo() {
+ return mGatewayInfo;
+ }
+
+ /**
+ * @return The video state of the {@code Call}.
+ */
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ /**
+ * @return The current {@link android.telecom.StatusHints}, or {@code null} if none
+ * have been set.
+ */
+ public StatusHints getStatusHints() {
+ return mStatusHints;
+ }
+
+ /**
+ * @return A bundle extras to pass with the call
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Details) {
+ Details d = (Details) o;
+ return
+ Objects.equals(mHandle, d.mHandle) &&
+ Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
+ Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
+ Objects.equals(mCallerDisplayNamePresentation,
+ d.mCallerDisplayNamePresentation) &&
+ Objects.equals(mAccountHandle, d.mAccountHandle) &&
+ Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
+ Objects.equals(mCallProperties, d.mCallProperties) &&
+ Objects.equals(mDisconnectCause, d.mDisconnectCause) &&
+ Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
+ Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
+ Objects.equals(mVideoState, d.mVideoState) &&
+ Objects.equals(mStatusHints, d.mStatusHints) &&
+ Objects.equals(mExtras, d.mExtras);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return
+ Objects.hashCode(mHandle) +
+ Objects.hashCode(mHandlePresentation) +
+ Objects.hashCode(mCallerDisplayName) +
+ Objects.hashCode(mCallerDisplayNamePresentation) +
+ Objects.hashCode(mAccountHandle) +
+ Objects.hashCode(mCallCapabilities) +
+ Objects.hashCode(mCallProperties) +
+ Objects.hashCode(mDisconnectCause) +
+ Objects.hashCode(mConnectTimeMillis) +
+ Objects.hashCode(mGatewayInfo) +
+ Objects.hashCode(mVideoState) +
+ Objects.hashCode(mStatusHints) +
+ Objects.hashCode(mExtras);
+ }
+
+ /** {@hide} */
+ public Details(
+ Uri handle,
+ int handlePresentation,
+ String callerDisplayName,
+ int callerDisplayNamePresentation,
+ PhoneAccountHandle accountHandle,
+ int capabilities,
+ int properties,
+ DisconnectCause disconnectCause,
+ long connectTimeMillis,
+ GatewayInfo gatewayInfo,
+ int videoState,
+ StatusHints statusHints,
+ Bundle extras) {
+ mHandle = handle;
+ mHandlePresentation = handlePresentation;
+ mCallerDisplayName = callerDisplayName;
+ mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+ mAccountHandle = accountHandle;
+ mCallCapabilities = capabilities;
+ mCallProperties = properties;
+ mDisconnectCause = disconnectCause;
+ mConnectTimeMillis = connectTimeMillis;
+ mGatewayInfo = gatewayInfo;
+ mVideoState = videoState;
+ mStatusHints = statusHints;
+ mExtras = extras;
+ }
+ }
+
+ public static abstract class Listener {
+ /**
+ * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param state The new state of the {@code Call}.
+ */
+ public void onStateChanged(Call call, int state) {}
+
+ /**
+ * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param parent The new parent of the {@code Call}.
+ */
+ public void onParentChanged(Call call, Call parent) {}
+
+ /**
+ * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param children The new children of the {@code Call}.
+ */
+ public void onChildrenChanged(Call call, List<Call> children) {}
+
+ /**
+ * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param details A {@code Details} object describing the {@code Call}.
+ */
+ public void onDetailsChanged(Call call, Details details) {}
+
+ /**
+ * Invoked when the text messages that can be used as responses to the incoming
+ * {@code Call} are loaded from the relevant database.
+ * See {@link #getCannedTextResponses()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param cannedTextResponses The text messages useable as responses.
+ */
+ public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
+
+ /**
+ * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
+ * character. This causes the post-dial signals to stop pending user confirmation. An
+ * implementation should present this choice to the user and invoke
+ * {@link #postDialContinue(boolean)} when the user makes the choice.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param remainingPostDialSequence The post-dial characters that remain to be sent.
+ */
+ public void onPostDialWait(Call call, String remainingPostDialSequence) {}
+
+ /**
+ * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
+ * @hide
+ */
+ public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
+
+ /**
+ * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
+ * up their UI for the {@code Call} in response to state transitions. Specifically,
+ * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
+ * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
+ * clients should wait for this method to be invoked.
+ *
+ * @param call The {@code Call} being destroyed.
+ */
+ public void onCallDestroyed(Call call) {}
+
+ /**
+ * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be
+ * conferenced.
+ *
+ * @param call The {@code Call} being updated.
+ * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be
+ * conferenced.
+ */
+ public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
+ }
+
+ private final Phone mPhone;
+ private final String mTelecomCallId;
+ private final InCallAdapter mInCallAdapter;
+ private final List<String> mChildrenIds = new ArrayList<>();
+ private final List<Call> mChildren = new ArrayList<>();
+ private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
+ private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
+ private final List<Call> mConferenceableCalls = new ArrayList<>();
+ private final List<Call> mUnmodifiableConferenceableCalls =
+ Collections.unmodifiableList(mConferenceableCalls);
+
+ private boolean mChildrenCached;
+ private String mParentId = null;
+ private int mState;
+ private List<String> mCannedTextResponses = null;
+ private String mRemainingPostDialSequence;
+ private InCallService.VideoCall mVideoCall;
+ private Details mDetails;
+
+ /**
+ * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
+ *
+ * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
+ * remaining or this {@code Call} is not in a post-dial state.
+ */
+ public String getRemainingPostDialSequence() {
+ return mRemainingPostDialSequence;
+ }
+
+ /**
+ * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
+ * @param videoState The video state in which to answer the call.
+ */
+ public void answer(int videoState) {
+ mInCallAdapter.answerCall(mTelecomCallId, videoState);
+ }
+
+ /**
+ * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
+ *
+ * @param rejectWithMessage Whether to reject with a text message.
+ * @param textMessage An optional text message with which to respond.
+ */
+ public void reject(boolean rejectWithMessage, String textMessage) {
+ mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage);
+ }
+
+ /**
+ * Instructs this {@code Call} to disconnect.
+ */
+ public void disconnect() {
+ mInCallAdapter.disconnectCall(mTelecomCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to go on hold.
+ */
+ public void hold() {
+ mInCallAdapter.holdCall(mTelecomCallId);
+ }
+
+ /**
+ * Instructs this {@link #STATE_HOLDING} call to release from hold.
+ */
+ public void unhold() {
+ mInCallAdapter.unholdCall(mTelecomCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
+ *
+ * Any other currently playing DTMF tone in the specified call is immediately stopped.
+ *
+ * @param digit A character representing the DTMF digit for which to play the tone. This
+ * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
+ */
+ public void playDtmfTone(char digit) {
+ mInCallAdapter.playDtmfTone(mTelecomCallId, digit);
+ }
+
+ /**
+ * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
+ * currently playing.
+ *
+ * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
+ * currently playing, this method will do nothing.
+ */
+ public void stopDtmfTone() {
+ mInCallAdapter.stopDtmfTone(mTelecomCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to continue playing a post-dial DTMF string.
+ *
+ * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
+ * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
+ *
+ * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
+ * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
+ *
+ * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
+ * {@code Call} will pause playing the tones and notify listeners via
+ * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app
+ * should display to the user an indication of this state and an affordance to continue
+ * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
+ * app should invoke the {@link #postDialContinue(boolean)} method.
+ *
+ * @param proceed Whether or not to continue with the post-dial sequence.
+ */
+ public void postDialContinue(boolean proceed) {
+ mInCallAdapter.postDialContinue(mTelecomCallId, proceed);
+ }
+
+ /**
+ * Notifies this {@code Call} that an account has been selected and to proceed with placing
+ * an outgoing call.
+ */
+ public void phoneAccountSelected(PhoneAccountHandle accountHandle) {
+ mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle);
+
+ }
+
+ /**
+ * Instructs this {@code Call} to enter a conference.
+ *
+ * @param callToConferenceWith The other call with which to conference.
+ */
+ public void conference(Call callToConferenceWith) {
+ if (callToConferenceWith != null) {
+ mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId);
+ }
+ }
+
+ /**
+ * Instructs this {@code Call} to split from any conference call with which it may be
+ * connected.
+ */
+ public void splitFromConference() {
+ mInCallAdapter.splitFromConference(mTelecomCallId);
+ }
+
+ /**
+ * Merges the calls within this conference. See {@link PhoneCapabilities#MERGE_CONFERENCE}.
+ */
+ public void mergeConference() {
+ mInCallAdapter.mergeConference(mTelecomCallId);
+ }
+
+ /**
+ * Swaps the calls within this conference. See {@link PhoneCapabilities#SWAP_CONFERENCE}.
+ */
+ public void swapConference() {
+ mInCallAdapter.swapConference(mTelecomCallId);
+ }
+
+ /**
+ * Obtains the parent of this {@code Call} in a conference, if any.
+ *
+ * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
+ * child of any conference {@code Call}s.
+ */
+ public Call getParent() {
+ if (mParentId != null) {
+ return mPhone.internalGetCallByTelecomId(mParentId);
+ }
+ return null;
+ }
+
+ /**
+ * Obtains the children of this conference {@code Call}, if any.
+ *
+ * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
+ * {@code List} otherwise.
+ */
+ public List<Call> getChildren() {
+ if (!mChildrenCached) {
+ mChildrenCached = true;
+ mChildren.clear();
+
+ for(String id : mChildrenIds) {
+ Call call = mPhone.internalGetCallByTelecomId(id);
+ if (call == null) {
+ // At least one child was still not found, so do not save true for "cached"
+ mChildrenCached = false;
+ } else {
+ mChildren.add(call);
+ }
+ }
+ }
+
+ return mUnmodifiableChildren;
+ }
+
+ /**
+ * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference.
+ *
+ * @return The list of conferenceable {@code Call}s.
+ */
+ public List<Call> getConferenceableCalls() {
+ return mUnmodifiableConferenceableCalls;
+ }
+
+ /**
+ * Obtains the state of this {@code Call}.
+ *
+ * @return A state value, chosen from the {@code STATE_*} constants.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Obtains a list of canned, pre-configured message responses to present to the user as
+ * ways of rejecting this {@code Call} using via a text message.
+ *
+ * @see #reject(boolean, String)
+ *
+ * @return A list of canned text message responses.
+ */
+ public List<String> getCannedTextResponses() {
+ return mCannedTextResponses;
+ }
+
+ /**
+ * Obtains an object that can be used to display video from this {@code Call}.
+ *
+ * @return An {@code Call.VideoCall}.
+ * @hide
+ */
+ public InCallService.VideoCall getVideoCall() {
+ return mVideoCall;
+ }
+
+ /**
+ * Obtains an object containing call details.
+ *
+ * @return A {@link Details} object. Depending on the state of the {@code Call}, the
+ * result may be {@code null}.
+ */
+ public Details getDetails() {
+ return mDetails;
+ }
+
+ /**
+ * Adds a listener to this {@code Call}.
+ *
+ * @param listener A {@code Listener}.
+ */
+ public void addListener(Listener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener from this {@code Call}.
+ *
+ * @param listener A {@code Listener}.
+ */
+ public void removeListener(Listener listener) {
+ if (listener != null) {
+ mListeners.remove(listener);
+ }
+ }
+
+ /** {@hide} */
+ Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
+ mPhone = phone;
+ mTelecomCallId = telecomCallId;
+ mInCallAdapter = inCallAdapter;
+ mState = STATE_NEW;
+ }
+
+ /** {@hide} */
+ final String internalGetCallId() {
+ return mTelecomCallId;
+ }
+
+ /** {@hide} */
+ final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
+ // First, we update the internal state as far as possible before firing any updates.
+ Details details = new Details(
+ parcelableCall.getHandle(),
+ parcelableCall.getHandlePresentation(),
+ parcelableCall.getCallerDisplayName(),
+ parcelableCall.getCallerDisplayNamePresentation(),
+ parcelableCall.getAccountHandle(),
+ parcelableCall.getCapabilities(),
+ parcelableCall.getProperties(),
+ parcelableCall.getDisconnectCause(),
+ parcelableCall.getConnectTimeMillis(),
+ parcelableCall.getGatewayInfo(),
+ parcelableCall.getVideoState(),
+ parcelableCall.getStatusHints(),
+ parcelableCall.getExtras());
+ boolean detailsChanged = !Objects.equals(mDetails, details);
+ if (detailsChanged) {
+ mDetails = details;
+ }
+
+ boolean cannedTextResponsesChanged = false;
+ if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
+ && !parcelableCall.getCannedSmsResponses().isEmpty()) {
+ mCannedTextResponses =
+ Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
+ }
+
+ boolean videoCallChanged = !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
+ if (videoCallChanged) {
+ mVideoCall = parcelableCall.getVideoCall();
+ }
+
+ int state = stateFromParcelableCallState(parcelableCall.getState());
+ boolean stateChanged = mState != state;
+ if (stateChanged) {
+ mState = state;
+ }
+
+ String parentId = parcelableCall.getParentCallId();
+ boolean parentChanged = !Objects.equals(mParentId, parentId);
+ if (parentChanged) {
+ mParentId = parentId;
+ }
+
+ List<String> childCallIds = parcelableCall.getChildCallIds();
+ boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds);
+ if (childrenChanged) {
+ mChildrenIds.clear();
+ mChildrenIds.addAll(parcelableCall.getChildCallIds());
+ mChildrenCached = false;
+ }
+
+ List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds();
+ List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size());
+ for (String otherId : conferenceableCallIds) {
+ if (callIdMap.containsKey(otherId)) {
+ conferenceableCalls.add(callIdMap.get(otherId));
+ }
+ }
+
+ if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) {
+ mConferenceableCalls.clear();
+ mConferenceableCalls.addAll(conferenceableCalls);
+ fireConferenceableCallsChanged();
+ }
+
+ // Now we fire updates, ensuring that any client who listens to any of these notifications
+ // gets the most up-to-date state.
+
+ if (stateChanged) {
+ fireStateChanged(mState);
+ }
+ if (detailsChanged) {
+ fireDetailsChanged(mDetails);
+ }
+ if (cannedTextResponsesChanged) {
+ fireCannedTextResponsesLoaded(mCannedTextResponses);
+ }
+ if (videoCallChanged) {
+ fireVideoCallChanged(mVideoCall);
+ }
+ if (parentChanged) {
+ fireParentChanged(getParent());
+ }
+ if (childrenChanged) {
+ fireChildrenChanged(getChildren());
+ }
+
+ // If we have transitioned to DISCONNECTED, that means we need to notify clients and
+ // remove ourselves from the Phone. Note that we do this after completing all state updates
+ // so a client can cleanly transition all their UI to the state appropriate for a
+ // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
+ if (mState == STATE_DISCONNECTED) {
+ fireCallDestroyed();
+ mPhone.internalRemoveCall(this);
+ }
+ }
+
+ /** {@hide} */
+ final void internalSetPostDialWait(String remaining) {
+ mRemainingPostDialSequence = remaining;
+ firePostDialWait(mRemainingPostDialSequence);
+ }
+
+ /** {@hide} */
+ final void internalSetDisconnected() {
+ if (mState != Call.STATE_DISCONNECTED) {
+ mState = Call.STATE_DISCONNECTED;
+ fireStateChanged(mState);
+ fireCallDestroyed();
+ mPhone.internalRemoveCall(this);
+ }
+ }
+
+ private void fireStateChanged(int newState) {
+ for (Listener listener : mListeners) {
+ listener.onStateChanged(this, newState);
+ }
+ }
+
+ private void fireParentChanged(Call newParent) {
+ for (Listener listener : mListeners) {
+ listener.onParentChanged(this, newParent);
+ }
+ }
+
+ private void fireChildrenChanged(List<Call> children) {
+ for (Listener listener : mListeners) {
+ listener.onChildrenChanged(this, children);
+ }
+ }
+
+ private void fireDetailsChanged(Details details) {
+ for (Listener listener : mListeners) {
+ listener.onDetailsChanged(this, details);
+ }
+ }
+
+ private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
+ for (Listener listener : mListeners) {
+ listener.onCannedTextResponsesLoaded(this, cannedTextResponses);
+ }
+ }
+
+ private void fireVideoCallChanged(InCallService.VideoCall videoCall) {
+ for (Listener listener : mListeners) {
+ listener.onVideoCallChanged(this, videoCall);
+ }
+ }
+
+ private void firePostDialWait(String remainingPostDialSequence) {
+ for (Listener listener : mListeners) {
+ listener.onPostDialWait(this, remainingPostDialSequence);
+ }
+ }
+
+ private void fireCallDestroyed() {
+ for (Listener listener : mListeners) {
+ listener.onCallDestroyed(this);
+ }
+ }
+
+ private void fireConferenceableCallsChanged() {
+ for (Listener listener : mListeners) {
+ listener.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
+ }
+ }
+
+ private int stateFromParcelableCallState(int parcelableCallState) {
+ switch (parcelableCallState) {
+ case CallState.NEW:
+ return STATE_NEW;
+ case CallState.CONNECTING:
+ return STATE_CONNECTING;
+ case CallState.PRE_DIAL_WAIT:
+ return STATE_PRE_DIAL_WAIT;
+ case CallState.DIALING:
+ return STATE_DIALING;
+ case CallState.RINGING:
+ return STATE_RINGING;
+ case CallState.ACTIVE:
+ return STATE_ACTIVE;
+ case CallState.ON_HOLD:
+ return STATE_HOLDING;
+ case CallState.DISCONNECTED:
+ return STATE_DISCONNECTED;
+ case CallState.ABORTED:
+ return STATE_DISCONNECTED;
+ default:
+ Log.wtf(this, "Unrecognized CallState %s", parcelableCallState);
+ return STATE_NEW;
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/CallProperties.java b/telecomm/java/android/telecom/CallProperties.java
new file mode 100644
index 0000000..b1b82e2
--- /dev/null
+++ b/telecomm/java/android/telecom/CallProperties.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * Defines properties of a phone call which may be affected by changes to the call.
+ * @hide
+ */
+public class CallProperties {
+ /** Call is currently in a conference call. */
+ public static final int CONFERENCE = 0x00000001;
+}
diff --git a/telecomm/java/android/telecom/CallState.java b/telecomm/java/android/telecom/CallState.java
new file mode 100644
index 0000000..7690847
--- /dev/null
+++ b/telecomm/java/android/telecom/CallState.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 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 android.annotation.SystemApi;
+
+/**
+ * Defines call-state constants of the different states in which a call can exist. Although states
+ * have the notion of normal transitions, due to the volatile nature of telephony systems, code
+ * that uses these states should be resilient to unexpected state changes outside of what is
+ * considered traditional.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class CallState {
+
+ private CallState() {}
+
+ /**
+ * Indicates that a call is new and not connected. This is used as the default state internally
+ * within Telecom and should not be used between Telecom and call services. Call services are
+ * not expected to ever interact with NEW calls, but {@link InCallService}s will see calls in
+ * this state.
+ */
+ public static final int NEW = 0;
+
+ /**
+ * The initial state of an outgoing {@code Call}.
+ * Common transitions are to {@link #DIALING} state for a successful call or
+ * {@link #DISCONNECTED} if it failed.
+ */
+ public static final int CONNECTING = 1;
+
+ /**
+ * Indicates that the call is about to go into the outgoing and dialing state but is waiting for
+ * user input before it proceeds. For example, where no default {@link PhoneAccount} is set,
+ * this is the state where the InCallUI is waiting for the user to select a
+ * {@link PhoneAccount} to call from.
+ */
+ public static final int PRE_DIAL_WAIT = 2;
+
+ /**
+ * Indicates that a call is outgoing and in the dialing state. A call transitions to this state
+ * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this
+ * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED}
+ * if the call was disconnected somehow (e.g., failure or cancellation of the call by the user).
+ */
+ public static final int DIALING = 3;
+
+ /**
+ * Indicates that a call is incoming and the user still has the option of answering, rejecting,
+ * or doing nothing with the call. This state is usually associated with some type of audible
+ * ringtone. Normal transitions are to {@link #ACTIVE} if answered or {@link #DISCONNECTED}
+ * otherwise.
+ */
+ public static final int RINGING = 4;
+
+ /**
+ * Indicates that a call is currently connected to another party and a communication channel is
+ * open between them. The normal transition to this state is by the user answering a
+ * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party.
+ */
+ public static final int ACTIVE = 5;
+
+ /**
+ * Indicates that the call is currently on hold. In this state, the call is not terminated
+ * but no communication is allowed until the call is no longer on hold. The typical transition
+ * to this state is by the user putting an {@link #ACTIVE} call on hold by explicitly performing
+ * an action, such as clicking the hold button.
+ */
+ public static final int ON_HOLD = 6;
+
+ /**
+ * Indicates that a call is currently disconnected. All states can transition to this state
+ * by the call service giving notice that the connection has been severed. When the user
+ * explicitly ends a call, it will not transition to this state until the call service confirms
+ * the disconnection or communication was lost to the call service currently responsible for
+ * this call (e.g., call service crashes).
+ */
+ public static final int DISCONNECTED = 7;
+
+ /**
+ * Indicates that the call was attempted (mostly in the context of outgoing, at least at the
+ * time of writing) but cancelled before it was successfully connected.
+ */
+ public static final int ABORTED = 8;
+
+ public static String toString(int callState) {
+ switch (callState) {
+ case NEW:
+ return "NEW";
+ case CONNECTING:
+ return "CONNECTING";
+ case PRE_DIAL_WAIT:
+ return "PRE_DIAL_WAIT";
+ case DIALING:
+ return "DIALING";
+ case RINGING:
+ return "RINGING";
+ case ACTIVE:
+ return "ACTIVE";
+ case ON_HOLD:
+ return "ON_HOLD";
+ case DISCONNECTED:
+ return "DISCONNECTED";
+ case ABORTED:
+ return "ABORTED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/CameraCapabilities.aidl b/telecomm/java/android/telecom/CameraCapabilities.aidl
new file mode 100644
index 0000000..c8e0c5e
--- /dev/null
+++ b/telecomm/java/android/telecom/CameraCapabilities.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+parcelable CameraCapabilities;
diff --git a/telecomm/java/android/telecom/CameraCapabilities.java b/telecomm/java/android/telecom/CameraCapabilities.java
new file mode 100644
index 0000000..f968c13
--- /dev/null
+++ b/telecomm/java/android/telecom/CameraCapabilities.java
@@ -0,0 +1,144 @@
+/*
+ * 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 android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents the camera capabilities important to a Video Telephony provider.
+ * @hide
+ */
+public final class CameraCapabilities implements Parcelable {
+
+ /**
+ * Whether the camera supports zoom.
+ */
+ private final boolean mZoomSupported;
+
+ /**
+ * The maximum zoom supported by the camera.
+ */
+ private final float mMaxZoom;
+
+ /**
+ * The width of the camera video in pixels.
+ */
+ private final int mWidth;
+
+ /**
+ * The height of the camera video in pixels.
+ */
+ private final int mHeight;
+
+ /**
+ * Create a call camera capabilities instance.
+ *
+ * @param zoomSupported True when camera supports zoom.
+ * @param maxZoom Maximum zoom supported by camera.
+ * @param width The width of the camera video (in pixels).
+ * @param height The height of the camera video (in pixels).
+ */
+ public CameraCapabilities(boolean zoomSupported, float maxZoom, int width, int height) {
+ mZoomSupported = zoomSupported;
+ mMaxZoom = maxZoom;
+ mWidth = width;
+ mHeight = height;
+ }
+
+ /**
+ * Responsible for creating CallCameraCapabilities objects from deserialized Parcels.
+ **/
+ public static final Parcelable.Creator<CameraCapabilities> CREATOR =
+ new Parcelable.Creator<CameraCapabilities> () {
+ /**
+ * Creates a CallCameraCapabilities instances from a parcel.
+ *
+ * @param source The parcel.
+ * @return The CallCameraCapabilities.
+ */
+ @Override
+ public CameraCapabilities createFromParcel(Parcel source) {
+ boolean supportsZoom = source.readByte() != 0;
+ float maxZoom = source.readFloat();
+ int width = source.readInt();
+ int height = source.readInt();
+
+ return new CameraCapabilities(supportsZoom, maxZoom, width, height);
+ }
+
+ @Override
+ public CameraCapabilities[] newArray(int size) {
+ return new CameraCapabilities[size];
+ }
+ };
+
+ /**
+ * Describe the kinds of special objects contained in this Parcelable's
+ * marshalled representation.
+ *
+ * @return a bitmask indicating the set of special object types marshalled
+ * by the Parcelable.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (isZoomSupported() ? 1 : 0));
+ dest.writeFloat(getMaxZoom());
+ dest.writeInt(getWidth());
+ dest.writeInt(getHeight());
+ }
+
+ /**
+ * Whether the camera supports zoom.
+ */
+ public boolean isZoomSupported() {
+ return mZoomSupported;
+ }
+
+ /**
+ * The maximum zoom supported by the camera.
+ */
+ public float getMaxZoom() {
+ return mMaxZoom;
+ }
+
+ /**
+ * The width of the camera video in pixels.
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * The height of the camera video in pixels.
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
new file mode 100644
index 0000000..9b350c1
--- /dev/null
+++ b/telecomm/java/android/telecom/Conference.java
@@ -0,0 +1,312 @@
+/*
+ * 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 java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+/**
+ * Represents a conference call which can contain any number of {@link Connection} objects.
+ */
+public abstract class Conference {
+
+ /** @hide */
+ public abstract static class Listener {
+ public void onStateChanged(Conference conference, int oldState, int newState) {}
+ public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {}
+ public void onConnectionAdded(Conference conference, Connection connection) {}
+ public void onConnectionRemoved(Conference conference, Connection connection) {}
+ public void onDestroyed(Conference conference) {}
+ public void onCapabilitiesChanged(Conference conference, int capabilities) {}
+ }
+
+ private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
+ private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>();
+ private final List<Connection> mUnmodifiableChildConnections =
+ Collections.unmodifiableList(mChildConnections);
+
+ private PhoneAccountHandle mPhoneAccount;
+ private AudioState mAudioState;
+ private int mState = Connection.STATE_NEW;
+ private DisconnectCause mDisconnectCause;
+ private int mCapabilities;
+ private String mDisconnectMessage;
+
+ /**
+ * Constructs a new Conference with a mandatory {@link PhoneAccountHandle}
+ *
+ * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference.
+ */
+ public Conference(PhoneAccountHandle phoneAccount) {
+ mPhoneAccount = phoneAccount;
+ }
+
+ /**
+ * Returns the {@link PhoneAccountHandle} the conference call is being placed through.
+ *
+ * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference.
+ */
+ public final PhoneAccountHandle getPhoneAccountHandle() {
+ return mPhoneAccount;
+ }
+
+ /**
+ * Returns the list of connections currently associated with the conference call.
+ *
+ * @return A list of {@code Connection} objects which represent the children of the conference.
+ */
+ public final List<Connection> getConnections() {
+ return mUnmodifiableChildConnections;
+ }
+
+ /**
+ * Gets the state of the conference call. See {@link Connection} for valid values.
+ *
+ * @return A constant representing the state the conference call is currently in.
+ */
+ public final int getState() {
+ return mState;
+ }
+
+ /**
+ * Returns the capabilities of a conference. See {@link PhoneCapabilities} for valid values.
+ *
+ * @return A bitmask of the {@code PhoneCapabilities} of the conference call.
+ */
+ public final int getCapabilities() {
+ return mCapabilities;
+ }
+
+ /**
+ * @return The audio state of the conference, describing how its audio is currently
+ * being routed by the system. This is {@code null} if this Conference
+ * does not directly know about its audio state.
+ */
+ public final AudioState getAudioState() {
+ return mAudioState;
+ }
+
+ /**
+ * Invoked when the Conference and all it's {@link Connection}s should be disconnected.
+ */
+ public void onDisconnect() {}
+
+ /**
+ * Invoked when the specified {@link Connection} should be separated from the conference call.
+ *
+ * @param connection The connection to separate.
+ */
+ public void onSeparate(Connection connection) {}
+
+ /**
+ * Invoked when the conference should be put on hold.
+ */
+ public void onHold() {}
+
+ /**
+ * Invoked when the conference should be moved from hold to active.
+ */
+ public void onUnhold() {}
+
+ /**
+ * Invoked when the child calls should be merged. Only invoked if the conference contains the
+ * capability {@link PhoneCapabilities#MERGE_CONFERENCE}.
+ */
+ public void onMerge() {}
+
+ /**
+ * Invoked when the child calls should be swapped. Only invoked if the conference contains the
+ * capability {@link PhoneCapabilities#SWAP_CONFERENCE}.
+ */
+ public void onSwap() {}
+
+ /**
+ * Notifies this conference of a request to play a DTMF tone.
+ *
+ * @param c A DTMF character.
+ */
+ public void onPlayDtmfTone(char c) {}
+
+ /**
+ * Notifies this conference of a request to stop any currently playing DTMF tones.
+ */
+ public void onStopDtmfTone() {}
+
+ /**
+ * Notifies this conference that the {@link #getAudioState()} property has a new value.
+ *
+ * @param state The new call audio state.
+ */
+ public void onAudioStateChanged(AudioState state) {}
+
+ /**
+ * Sets state to be on hold.
+ */
+ public final void setOnHold() {
+ setState(Connection.STATE_HOLDING);
+ }
+
+ /**
+ * Sets state to be active.
+ */
+ public final void setActive() {
+ setState(Connection.STATE_ACTIVE);
+ }
+
+ /**
+ * Sets state to disconnected.
+ *
+ * @param disconnectCause The reason for the disconnection, as described by
+ * {@link android.telecom.DisconnectCause}.
+ */
+ public final void setDisconnected(DisconnectCause disconnectCause) {
+ mDisconnectCause = disconnectCause;;
+ setState(Connection.STATE_DISCONNECTED);
+ for (Listener l : mListeners) {
+ l.onDisconnected(this, mDisconnectCause);
+ }
+ }
+
+ /**
+ * Sets the capabilities of a conference. See {@link PhoneCapabilities} for valid values.
+ *
+ * @param capabilities A bitmask of the {@code PhoneCapabilities} of the conference call.
+ */
+ public final void setCapabilities(int capabilities) {
+ if (capabilities != mCapabilities) {
+ mCapabilities = capabilities;
+
+ for (Listener l : mListeners) {
+ l.onCapabilitiesChanged(this, mCapabilities);
+ }
+ }
+ }
+
+ /**
+ * Adds the specified connection as a child of this conference.
+ *
+ * @param connection The connection to add.
+ * @return True if the connection was successfully added.
+ */
+ public final boolean addConnection(Connection connection) {
+ if (connection != null && !mChildConnections.contains(connection)) {
+ if (connection.setConference(this)) {
+ mChildConnections.add(connection);
+ for (Listener l : mListeners) {
+ l.onConnectionAdded(this, connection);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes the specified connection as a child of this conference.
+ *
+ * @param connection The connection to remove.
+ */
+ public final void removeConnection(Connection connection) {
+ Log.d(this, "removing %s from %s", connection, mChildConnections);
+ if (connection != null && mChildConnections.remove(connection)) {
+ connection.resetConference();
+ for (Listener l : mListeners) {
+ l.onConnectionRemoved(this, connection);
+ }
+ }
+ }
+
+ /**
+ * Tears down the conference object and any of its current connections.
+ */
+ public final void destroy() {
+ Log.d(this, "destroying conference : %s", this);
+ // Tear down the children.
+ for (Connection connection : mChildConnections) {
+ Log.d(this, "removing connection %s", connection);
+ removeConnection(connection);
+ }
+
+ // If not yet disconnected, set the conference call as disconnected first.
+ if (mState != Connection.STATE_DISCONNECTED) {
+ Log.d(this, "setting to disconnected");
+ setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
+ }
+
+ // ...and notify.
+ for (Listener l : mListeners) {
+ l.onDestroyed(this);
+ }
+ }
+
+ /**
+ * Add a listener to be notified of a state change.
+ *
+ * @param listener The new listener.
+ * @return This conference.
+ * @hide
+ */
+ public final Conference addListener(Listener listener) {
+ mListeners.add(listener);
+ return this;
+ }
+
+ /**
+ * Removes the specified listener.
+ *
+ * @param listener The listener to remove.
+ * @return This conference.
+ * @hide
+ */
+ public final Conference removeListener(Listener listener) {
+ mListeners.remove(listener);
+ return this;
+ }
+
+ /**
+ * Inform this Conference 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);
+ }
+
+ private void setState(int newState) {
+ if (newState != Connection.STATE_ACTIVE &&
+ newState != Connection.STATE_HOLDING &&
+ newState != Connection.STATE_DISCONNECTED) {
+ Log.w(this, "Unsupported state transition for Conference call.",
+ Connection.stateToString(newState));
+ return;
+ }
+
+ if (mState != newState) {
+ int oldState = mState;
+ mState = newState;
+ for (Listener l : mListeners) {
+ l.onStateChanged(this, oldState, newState);
+ }
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
new file mode 100644
index 0000000..7979e44
--- /dev/null
+++ b/telecomm/java/android/telecom/Connection.java
@@ -0,0 +1,1116 @@
+/*
+ * 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.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, DisconnectCause disconnectCause) {}
+ 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 DisconnectCause mDisconnectCause;
+ 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 DisconnectCause getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ /**
+ * 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 disconnectCause The reason for the disconnection, as specified by
+ * {@link DisconnectCause}.
+ */
+ public final void setDisconnected(DisconnectCause disconnectCause) {
+ mDisconnectCause = disconnectCause;
+ setState(STATE_DISCONNECTED);
+ Log.d(this, "Disconnected with cause %s", disconnectCause);
+ for (Listener l : mListeners) {
+ l.onDisconnected(this, disconnectCause);
+ }
+ }
+
+ /**
+ * 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(DisconnectCause disconnectCause) {
+ setDisconnected(disconnectCause);
+ }
+ }
+
+ /**
+ * Return a {@code Connection} which represents a failed connection attempt. The returned
+ * {@code Connection} will have a {@link android.telecom.DisconnectCause} and 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 disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
+ * @return A {@code Connection} which indicates failure.
+ */
+ public static Connection createFailedConnection(DisconnectCause disconnectCause) {
+ return new FailureSignalingConnection(disconnectCause);
+ }
+
+ /**
+ * 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(new DisconnectCause(DisconnectCause.CANCELED));
+ }
+
+ 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();
+ }
+}
diff --git a/telecomm/java/android/telecom/ConnectionRequest.aidl b/telecomm/java/android/telecom/ConnectionRequest.aidl
new file mode 100644
index 0000000..de39c67
--- /dev/null
+++ b/telecomm/java/android/telecom/ConnectionRequest.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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;
+
+/**
+ * {@hide}
+ */
+parcelable ConnectionRequest;
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
new file mode 100644
index 0000000..71b481b
--- /dev/null
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -0,0 +1,140 @@
+/*
+ * 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 android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Simple data container encapsulating a request to some entity to
+ * create a new {@link Connection}.
+ */
+public final class ConnectionRequest implements Parcelable {
+
+ // TODO: Token to limit recursive invocations
+ private final PhoneAccountHandle mAccountHandle;
+ private final Uri mAddress;
+ private final Bundle mExtras;
+ private final int mVideoState;
+
+ /**
+ * @param accountHandle The accountHandle which should be used to place the call.
+ * @param handle The handle (e.g., phone number) to which the {@link Connection} is to connect.
+ * @param extras Application-specific extra data.
+ */
+ public ConnectionRequest(
+ PhoneAccountHandle accountHandle,
+ Uri handle,
+ Bundle extras) {
+ this(accountHandle, handle, extras, VideoProfile.VideoState.AUDIO_ONLY);
+ }
+
+ /**
+ * @param accountHandle The accountHandle which should be used to place the call.
+ * @param handle The handle (e.g., phone number) to which the {@link Connection} is to connect.
+ * @param extras Application-specific extra data.
+ * @param videoState Determines the video state for the connection.
+ * @hide
+ */
+ public ConnectionRequest(
+ PhoneAccountHandle accountHandle,
+ Uri handle,
+ Bundle extras,
+ int videoState) {
+ mAccountHandle = accountHandle;
+ mAddress = handle;
+ mExtras = extras;
+ mVideoState = videoState;
+ }
+
+ private ConnectionRequest(Parcel in) {
+ mAccountHandle = in.readParcelable(getClass().getClassLoader());
+ mAddress = in.readParcelable(getClass().getClassLoader());
+ mExtras = in.readParcelable(getClass().getClassLoader());
+ mVideoState = in.readInt();
+ }
+
+ /**
+ * The account which should be used to place the call.
+ */
+ public PhoneAccountHandle getAccountHandle() { return mAccountHandle; }
+
+ /**
+ * The handle (e.g., phone number) to which the {@link Connection} is to connect.
+ */
+ public Uri getAddress() { return mAddress; }
+
+ /**
+ * Application-specific extra data. Used for passing back information from an incoming
+ * call {@code Intent}, and for any proprietary extensions arranged between a client
+ * and servant {@code ConnectionService} which agree on a vocabulary for such data.
+ */
+ public Bundle getExtras() { return mExtras; }
+
+ /**
+ * Describes the video states supported by the client requesting the connection.
+ * 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 for the connection.
+ * @hide
+ */
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("ConnectionRequest %s %s",
+ mAddress == null
+ ? Uri.EMPTY
+ : Connection.toLogSafePhoneNumber(mAddress.toString()),
+ mExtras == null ? "" : mExtras);
+ }
+
+ public static final Creator<ConnectionRequest> CREATOR = new Creator<ConnectionRequest> () {
+ @Override
+ public ConnectionRequest createFromParcel(Parcel source) {
+ return new ConnectionRequest(source);
+ }
+
+ @Override
+ public ConnectionRequest[] newArray(int size) {
+ return new ConnectionRequest[size];
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeParcelable(mAccountHandle, 0);
+ destination.writeParcelable(mAddress, 0);
+ destination.writeParcelable(mExtras, 0);
+ destination.writeInt(mVideoState);
+ }
+}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
new file mode 100644
index 0000000..3e18bac
--- /dev/null
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -0,0 +1,984 @@
+/*
+ * 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 android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.IConnectionService;
+import com.android.internal.telecom.IConnectionServiceAdapter;
+import com.android.internal.telecom.RemoteServiceCallback;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * A {@link android.app.Service} that provides telephone connections to processes running on an
+ * Android device.
+ */
+public abstract class ConnectionService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telecom.ConnectionService";
+
+ // Flag controlling whether PII is emitted into the logs
+ private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
+
+ private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
+ private static final int MSG_CREATE_CONNECTION = 2;
+ private static final int MSG_ABORT = 3;
+ private static final int MSG_ANSWER = 4;
+ private static final int MSG_REJECT = 5;
+ private static final int MSG_DISCONNECT = 6;
+ private static final int MSG_HOLD = 7;
+ private static final int MSG_UNHOLD = 8;
+ private static final int MSG_ON_AUDIO_STATE_CHANGED = 9;
+ private static final int MSG_PLAY_DTMF_TONE = 10;
+ private static final int MSG_STOP_DTMF_TONE = 11;
+ private static final int MSG_CONFERENCE = 12;
+ private static final int MSG_SPLIT_FROM_CONFERENCE = 13;
+ private static final int MSG_ON_POST_DIAL_CONTINUE = 14;
+ private static final int MSG_REMOVE_CONNECTION_SERVICE_ADAPTER = 16;
+ private static final int MSG_ANSWER_VIDEO = 17;
+ private static final int MSG_MERGE_CONFERENCE = 18;
+ private static final int MSG_SWAP_CONFERENCE = 19;
+
+ private static Connection sNullConnection;
+
+ private final Map<String, Connection> mConnectionById = new HashMap<>();
+ private final Map<Connection, String> mIdByConnection = new HashMap<>();
+ private final Map<String, Conference> mConferenceById = new HashMap<>();
+ private final Map<Conference, String> mIdByConference = new HashMap<>();
+ private final RemoteConnectionManager mRemoteConnectionManager =
+ new RemoteConnectionManager(this);
+ private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>();
+ private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
+
+ private boolean mAreAccountsInitialized = false;
+ private Conference sNullConference;
+
+ private final IBinder mBinder = new IConnectionService.Stub() {
+ @Override
+ public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
+ mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();
+ }
+
+ public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
+ mHandler.obtainMessage(MSG_REMOVE_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();
+ }
+
+ @Override
+ public void createConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ String id,
+ ConnectionRequest request,
+ boolean isIncoming) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionManagerPhoneAccount;
+ args.arg2 = id;
+ args.arg3 = request;
+ args.argi1 = isIncoming ? 1 : 0;
+ mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
+ }
+
+ @Override
+ public void abort(String callId) {
+ mHandler.obtainMessage(MSG_ABORT, callId).sendToTarget();
+ }
+
+ @Override
+ /** @hide */
+ public void answerVideo(String callId, int videoState) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.argi1 = videoState;
+ mHandler.obtainMessage(MSG_ANSWER_VIDEO, args).sendToTarget();
+ }
+
+ @Override
+ public void answer(String callId) {
+ mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
+ }
+
+ @Override
+ public void reject(String callId) {
+ mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget();
+ }
+
+ @Override
+ public void disconnect(String callId) {
+ mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
+ }
+
+ @Override
+ public void hold(String callId) {
+ mHandler.obtainMessage(MSG_HOLD, callId).sendToTarget();
+ }
+
+ @Override
+ public void unhold(String callId) {
+ mHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget();
+ }
+
+ @Override
+ public void onAudioStateChanged(String callId, AudioState audioState) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = audioState;
+ mHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget();
+ }
+
+ @Override
+ public void playDtmfTone(String callId, char digit) {
+ mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget();
+ }
+
+ @Override
+ public void stopDtmfTone(String callId) {
+ mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
+ }
+
+ @Override
+ public void conference(String callId1, String callId2) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId1;
+ args.arg2 = callId2;
+ mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
+ }
+
+ @Override
+ public void splitFromConference(String callId) {
+ mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
+ }
+
+ @Override
+ public void mergeConference(String callId) {
+ mHandler.obtainMessage(MSG_MERGE_CONFERENCE, callId).sendToTarget();
+ }
+
+ @Override
+ public void swapConference(String callId) {
+ mHandler.obtainMessage(MSG_SWAP_CONFERENCE, callId).sendToTarget();
+ }
+
+ @Override
+ public void onPostDialContinue(String callId, boolean proceed) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.argi1 = proceed ? 1 : 0;
+ mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
+ }
+ };
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ADD_CONNECTION_SERVICE_ADAPTER:
+ mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj);
+ onAdapterAttached();
+ break;
+ case MSG_REMOVE_CONNECTION_SERVICE_ADAPTER:
+ mAdapter.removeAdapter((IConnectionServiceAdapter) msg.obj);
+ break;
+ case MSG_CREATE_CONNECTION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ final PhoneAccountHandle connectionManagerPhoneAccount =
+ (PhoneAccountHandle) args.arg1;
+ final String id = (String) args.arg2;
+ final ConnectionRequest request = (ConnectionRequest) args.arg3;
+ final boolean isIncoming = args.argi1 == 1;
+ if (!mAreAccountsInitialized) {
+ Log.d(this, "Enqueueing pre-init request %s", id);
+ mPreInitializationConnectionRequests.add(new Runnable() {
+ @Override
+ public void run() {
+ createConnection(
+ connectionManagerPhoneAccount,
+ id,
+ request,
+ isIncoming);
+ }
+ });
+ } else {
+ createConnection(
+ connectionManagerPhoneAccount,
+ id,
+ request,
+ isIncoming);
+ }
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_ABORT:
+ abort((String) msg.obj);
+ break;
+ case MSG_ANSWER:
+ answer((String) msg.obj);
+ break;
+ case MSG_ANSWER_VIDEO: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ int videoState = args.argi1;
+ answerVideo(callId, videoState);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_REJECT:
+ reject((String) msg.obj);
+ break;
+ case MSG_DISCONNECT:
+ disconnect((String) msg.obj);
+ break;
+ case MSG_HOLD:
+ hold((String) msg.obj);
+ break;
+ case MSG_UNHOLD:
+ unhold((String) msg.obj);
+ break;
+ case MSG_ON_AUDIO_STATE_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ AudioState audioState = (AudioState) args.arg2;
+ onAudioStateChanged(callId, audioState);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_PLAY_DTMF_TONE:
+ playDtmfTone((String) msg.obj, (char) msg.arg1);
+ break;
+ case MSG_STOP_DTMF_TONE:
+ stopDtmfTone((String) msg.obj);
+ break;
+ case MSG_CONFERENCE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId1 = (String) args.arg1;
+ String callId2 = (String) args.arg2;
+ conference(callId1, callId2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SPLIT_FROM_CONFERENCE:
+ splitFromConference((String) msg.obj);
+ break;
+ case MSG_MERGE_CONFERENCE:
+ mergeConference((String) msg.obj);
+ break;
+ case MSG_SWAP_CONFERENCE:
+ swapConference((String) msg.obj);
+ break;
+ case MSG_ON_POST_DIAL_CONTINUE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ boolean proceed = (args.argi1 == 1);
+ onPostDialContinue(callId, proceed);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ };
+
+ private final Conference.Listener mConferenceListener = new Conference.Listener() {
+ @Override
+ public void onStateChanged(Conference conference, int oldState, int newState) {
+ String id = mIdByConference.get(conference);
+ switch (newState) {
+ case Connection.STATE_ACTIVE:
+ mAdapter.setActive(id);
+ break;
+ case Connection.STATE_HOLDING:
+ mAdapter.setOnHold(id);
+ break;
+ case Connection.STATE_DISCONNECTED:
+ // handled by onDisconnected
+ break;
+ }
+ }
+
+ @Override
+ public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {
+ String id = mIdByConference.get(conference);
+ mAdapter.setDisconnected(id, disconnectCause);
+ }
+
+ @Override
+ public void onConnectionAdded(Conference conference, Connection connection) {
+ }
+
+ @Override
+ public void onConnectionRemoved(Conference conference, Connection connection) {
+ }
+
+ @Override
+ public void onDestroyed(Conference conference) {
+ removeConference(conference);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(Conference conference, int capabilities) {
+ String id = mIdByConference.get(conference);
+ Log.d(this, "call capabilities: conference: %s",
+ PhoneCapabilities.toString(capabilities));
+ mAdapter.setCallCapabilities(id, capabilities);
+ }
+ };
+
+ private final Connection.Listener mConnectionListener = new Connection.Listener() {
+ @Override
+ public void onStateChanged(Connection c, int state) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state));
+ switch (state) {
+ case Connection.STATE_ACTIVE:
+ mAdapter.setActive(id);
+ break;
+ case Connection.STATE_DIALING:
+ mAdapter.setDialing(id);
+ break;
+ case Connection.STATE_DISCONNECTED:
+ // Handled in onDisconnected()
+ break;
+ case Connection.STATE_HOLDING:
+ mAdapter.setOnHold(id);
+ break;
+ case Connection.STATE_NEW:
+ // Nothing to tell Telecom
+ break;
+ case Connection.STATE_RINGING:
+ mAdapter.setRinging(id);
+ break;
+ }
+ }
+
+ @Override
+ public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "Adapter set disconnected %s", disconnectCause);
+ mAdapter.setDisconnected(id, disconnectCause);
+ }
+
+ @Override
+ public void onVideoStateChanged(Connection c, int videoState) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "Adapter set video state %d", videoState);
+ mAdapter.setVideoState(id, videoState);
+ }
+
+ @Override
+ public void onAddressChanged(Connection c, Uri address, int presentation) {
+ String id = mIdByConnection.get(c);
+ mAdapter.setAddress(id, address, presentation);
+ }
+
+ @Override
+ public void onCallerDisplayNameChanged(
+ Connection c, String callerDisplayName, int presentation) {
+ String id = mIdByConnection.get(c);
+ mAdapter.setCallerDisplayName(id, callerDisplayName, presentation);
+ }
+
+ @Override
+ public void onDestroyed(Connection c) {
+ removeConnection(c);
+ }
+
+ @Override
+ public void onPostDialWait(Connection c, String remaining) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining);
+ mAdapter.onPostDialWait(id, remaining);
+ }
+
+ @Override
+ public void onRingbackRequested(Connection c, boolean ringback) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "Adapter onRingback %b", ringback);
+ mAdapter.setRingbackRequested(id, ringback);
+ }
+
+ @Override
+ public void onCallCapabilitiesChanged(Connection c, int capabilities) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "capabilities: parcelableconnection: %s",
+ PhoneCapabilities.toString(capabilities));
+ mAdapter.setCallCapabilities(id, capabilities);
+ }
+
+ @Override
+ public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) {
+ String id = mIdByConnection.get(c);
+ mAdapter.setVideoProvider(id, videoProvider);
+ }
+
+ @Override
+ public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {
+ String id = mIdByConnection.get(c);
+ mAdapter.setIsVoipAudioMode(id, isVoip);
+ }
+
+ @Override
+ public void onStatusHintsChanged(Connection c, StatusHints statusHints) {
+ String id = mIdByConnection.get(c);
+ mAdapter.setStatusHints(id, statusHints);
+ }
+
+ @Override
+ public void onConferenceableConnectionsChanged(
+ Connection connection, List<Connection> conferenceableConnections) {
+ mAdapter.setConferenceableConnections(
+ mIdByConnection.get(connection),
+ createConnectionIdList(conferenceableConnections));
+ }
+
+ @Override
+ public void onConferenceChanged(Connection connection, Conference conference) {
+ String id = mIdByConnection.get(connection);
+ if (id != null) {
+ String conferenceId = null;
+ if (conference != null) {
+ conferenceId = mIdByConference.get(conference);
+ }
+ mAdapter.setIsConferenced(id, conferenceId);
+ }
+ }
+ };
+
+ /** {@inheritDoc} */
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onUnbind(Intent intent) {
+ endAllConnections();
+ return super.onUnbind(intent);
+ }
+
+ /**
+ * This can be used by telecom to either create a new outgoing call or attach to an existing
+ * incoming call. In either case, telecom will cycle through a set of services and call
+ * createConnection util a connection service cancels the process or completes it successfully.
+ */
+ private void createConnection(
+ final PhoneAccountHandle callManagerAccount,
+ final String callId,
+ final ConnectionRequest request,
+ boolean isIncoming) {
+ Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
+ "isIncoming: %b", callManagerAccount, callId, request, isIncoming);
+
+ Connection connection = isIncoming
+ ? onCreateIncomingConnection(callManagerAccount, request)
+ : onCreateOutgoingConnection(callManagerAccount, request);
+ Log.d(this, "createConnection, connection: %s", connection);
+ if (connection == null) {
+ connection = Connection.createFailedConnection(
+ new DisconnectCause(DisconnectCause.ERROR));
+ }
+
+ if (connection.getState() != Connection.STATE_DISCONNECTED) {
+ addConnection(callId, connection);
+ }
+
+ Uri address = connection.getAddress();
+ String number = address == null ? "null" : address.getSchemeSpecificPart();
+ Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s",
+ Connection.toLogSafePhoneNumber(number),
+ Connection.stateToString(connection.getState()),
+ PhoneCapabilities.toString(connection.getCallCapabilities()));
+
+ Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
+ mAdapter.handleCreateConnectionComplete(
+ callId,
+ request,
+ new ParcelableConnection(
+ request.getAccountHandle(),
+ connection.getState(),
+ connection.getCallCapabilities(),
+ connection.getAddress(),
+ connection.getAddressPresentation(),
+ connection.getCallerDisplayName(),
+ connection.getCallerDisplayNamePresentation(),
+ connection.getVideoProvider() == null ?
+ null : connection.getVideoProvider().getInterface(),
+ connection.getVideoState(),
+ connection.isRingbackRequested(),
+ connection.getAudioModeIsVoip(),
+ connection.getStatusHints(),
+ connection.getDisconnectCause(),
+ createConnectionIdList(connection.getConferenceableConnections())));
+ }
+
+ private void abort(String callId) {
+ Log.d(this, "abort %s", callId);
+ findConnectionForAction(callId, "abort").onAbort();
+ }
+
+ private void answerVideo(String callId, int videoState) {
+ Log.d(this, "answerVideo %s", callId);
+ findConnectionForAction(callId, "answer").onAnswer(videoState);
+ }
+
+ private void answer(String callId) {
+ Log.d(this, "answer %s", callId);
+ findConnectionForAction(callId, "answer").onAnswer();
+ }
+
+ private void reject(String callId) {
+ Log.d(this, "reject %s", callId);
+ findConnectionForAction(callId, "reject").onReject();
+ }
+
+ private void disconnect(String callId) {
+ Log.d(this, "disconnect %s", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "disconnect").onDisconnect();
+ } else {
+ findConferenceForAction(callId, "disconnect").onDisconnect();
+ }
+ }
+
+ private void hold(String callId) {
+ Log.d(this, "hold %s", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "hold").onHold();
+ } else {
+ findConferenceForAction(callId, "hold").onHold();
+ }
+ }
+
+ private void unhold(String callId) {
+ Log.d(this, "unhold %s", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "unhold").onUnhold();
+ } else {
+ findConferenceForAction(callId, "unhold").onUnhold();
+ }
+ }
+
+ private void onAudioStateChanged(String callId, AudioState audioState) {
+ Log.d(this, "onAudioStateChanged %s %s", callId, audioState);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
+ } else {
+ findConferenceForAction(callId, "onAudioStateChanged").setAudioState(audioState);
+ }
+ }
+
+ private void playDtmfTone(String callId, char digit) {
+ Log.d(this, "playDtmfTone %s %c", callId, digit);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
+ } else {
+ findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
+ }
+ }
+
+ private void stopDtmfTone(String callId) {
+ Log.d(this, "stopDtmfTone %s", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
+ } else {
+ findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone();
+ }
+ }
+
+ private void conference(String callId1, String callId2) {
+ Log.d(this, "conference %s, %s", callId1, callId2);
+
+ Connection connection1 = findConnectionForAction(callId1, "conference");
+ if (connection1 == getNullConnection()) {
+ Log.w(this, "Connection1 missing in conference request %s.", callId1);
+ return;
+ }
+
+ Connection connection2 = findConnectionForAction(callId2, "conference");
+ if (connection2 == getNullConnection()) {
+ Log.w(this, "Connection2 missing in conference request %s.", callId2);
+ return;
+ }
+
+ onConference(connection1, connection2);
+ }
+
+ private void splitFromConference(String callId) {
+ Log.d(this, "splitFromConference(%s)", callId);
+
+ Connection connection = findConnectionForAction(callId, "splitFromConference");
+ if (connection == getNullConnection()) {
+ Log.w(this, "Connection missing in conference request %s.", callId);
+ return;
+ }
+
+ Conference conference = connection.getConference();
+ if (conference != null) {
+ conference.onSeparate(connection);
+ }
+ }
+
+ private void mergeConference(String callId) {
+ Log.d(this, "mergeConference(%s)", callId);
+ Conference conference = findConferenceForAction(callId, "mergeConference");
+ if (conference != null) {
+ conference.onMerge();
+ }
+ }
+
+ private void swapConference(String callId) {
+ Log.d(this, "swapConference(%s)", callId);
+ Conference conference = findConferenceForAction(callId, "swapConference");
+ if (conference != null) {
+ conference.onSwap();
+ }
+ }
+
+ private void onPostDialContinue(String callId, boolean proceed) {
+ Log.d(this, "onPostDialContinue(%s)", callId);
+ findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
+ }
+
+ private void onAdapterAttached() {
+ if (mAreAccountsInitialized) {
+ // No need to query again if we already did it.
+ return;
+ }
+
+ mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
+ @Override
+ public void onResult(
+ final List<ComponentName> componentNames,
+ final List<IBinder> services) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < componentNames.size() && i < services.size(); i++) {
+ mRemoteConnectionManager.addConnectionService(
+ componentNames.get(i),
+ IConnectionService.Stub.asInterface(services.get(i)));
+ }
+ onAccountsInitialized();
+ Log.d(this, "remote connection services found: " + services);
+ }
+ });
+ }
+
+ @Override
+ public void onError() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAreAccountsInitialized = true;
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
+ * incoming request. This is used to attach to existing incoming calls.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request Details about the incoming call.
+ * @return The {@code Connection} object to satisfy this call, or {@code null} to
+ * not handle the call.
+ */
+ public final RemoteConnection createRemoteIncomingConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ return mRemoteConnectionManager.createRemoteConnection(
+ connectionManagerPhoneAccount, request, true);
+ }
+
+ /**
+ * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
+ * outgoing request. This is used to initiate new outgoing calls.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request Details about the incoming call.
+ * @return The {@code Connection} object to satisfy this call, or {@code null} to
+ * not handle the call.
+ */
+ public final RemoteConnection createRemoteOutgoingConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ return mRemoteConnectionManager.createRemoteConnection(
+ connectionManagerPhoneAccount, request, false);
+ }
+
+ /**
+ * Adds two {@code RemoteConnection}s to some {@code RemoteConference}.
+ */
+ public final void conferenceRemoteConnections(
+ RemoteConnection a,
+ RemoteConnection b) {
+ mRemoteConnectionManager.conferenceRemoteConnections(a, b);
+ }
+
+ /**
+ * Adds a new conference call. When a conference call is created either as a result of an
+ * explicit request via {@link #onConference} or otherwise, the connection service should supply
+ * an instance of {@link Conference} by invoking this method. A conference call provided by this
+ * method will persist until {@link Conference#destroy} is invoked on the conference instance.
+ *
+ * @param conference The new conference object.
+ */
+ public final void addConference(Conference conference) {
+ String id = addConferenceInternal(conference);
+ if (id != null) {
+ List<String> connectionIds = new ArrayList<>(2);
+ for (Connection connection : conference.getConnections()) {
+ if (mIdByConnection.containsKey(connection)) {
+ connectionIds.add(mIdByConnection.get(connection));
+ }
+ }
+ ParcelableConference parcelableConference = new ParcelableConference(
+ conference.getPhoneAccountHandle(),
+ conference.getState(),
+ conference.getCapabilities(),
+ connectionIds);
+ mAdapter.addConferenceCall(id, parcelableConference);
+
+ // Go through any child calls and set the parent.
+ for (Connection connection : conference.getConnections()) {
+ String connectionId = mIdByConnection.get(connection);
+ if (connectionId != null) {
+ mAdapter.setIsConferenced(connectionId, id);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns all the active {@code Connection}s for which this {@code ConnectionService}
+ * has taken responsibility.
+ *
+ * @return A collection of {@code Connection}s created by this {@code ConnectionService}.
+ */
+ public final Collection<Connection> getAllConnections() {
+ return mConnectionById.values();
+ }
+
+ /**
+ * Create a {@code Connection} given an incoming request. This is used to attach to existing
+ * incoming calls.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request Details about the incoming call.
+ * @return The {@code Connection} object to satisfy this call, or {@code null} to
+ * not handle the call.
+ */
+ public Connection onCreateIncomingConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ return null;
+ }
+
+ /**
+ * Create a {@code Connection} given an outgoing request. This is used to initiate new
+ * outgoing calls.
+ *
+ * @param connectionManagerPhoneAccount The connection manager account to use for managing
+ * this call.
+ * <p>
+ * If this parameter is not {@code null}, it means that this {@code ConnectionService}
+ * has registered one or more {@code PhoneAccount}s having
+ * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
+ * one of these {@code PhoneAccount}s, while the {@code request} will contain another
+ * (usually but not always distinct) {@code PhoneAccount} to be used for actually
+ * making the connection.
+ * <p>
+ * If this parameter is {@code null}, it means that this {@code ConnectionService} is
+ * being asked to make a direct connection. The
+ * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
+ * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
+ * making the connection.
+ * @param request Details about the outgoing call.
+ * @return The {@code Connection} object to satisfy this call, or the result of an invocation
+ * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+ */
+ public Connection onCreateOutgoingConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ return null;
+ }
+
+ /**
+ * Conference two specified connections. Invoked when the user has made a request to merge the
+ * specified connections into a conference call. In response, the connection service should
+ * create an instance of {@link Conference} and pass it into {@link #addConference}.
+ *
+ * @param connection1 A connection to merge into a conference call.
+ * @param connection2 A connection to merge into a conference call.
+ */
+ public void onConference(Connection connection1, Connection connection2) {}
+
+ public void onRemoteConferenceAdded(RemoteConference conference) {}
+
+ /**
+ * @hide
+ */
+ public boolean containsConference(Conference conference) {
+ return mIdByConference.containsKey(conference);
+ }
+
+ /** {@hide} */
+ void addRemoteConference(RemoteConference remoteConference) {
+ onRemoteConferenceAdded(remoteConference);
+ }
+
+ private void onAccountsInitialized() {
+ mAreAccountsInitialized = true;
+ for (Runnable r : mPreInitializationConnectionRequests) {
+ r.run();
+ }
+ mPreInitializationConnectionRequests.clear();
+ }
+
+ private void addConnection(String callId, Connection connection) {
+ mConnectionById.put(callId, connection);
+ mIdByConnection.put(connection, callId);
+ connection.addConnectionListener(mConnectionListener);
+ connection.setConnectionService(this);
+ }
+
+ private void removeConnection(Connection connection) {
+ String id = mIdByConnection.get(connection);
+ connection.unsetConnectionService(this);
+ connection.removeConnectionListener(mConnectionListener);
+ mConnectionById.remove(mIdByConnection.get(connection));
+ mIdByConnection.remove(connection);
+ mAdapter.removeCall(id);
+ }
+
+ private String addConferenceInternal(Conference conference) {
+ if (mIdByConference.containsKey(conference)) {
+ Log.w(this, "Re-adding an existing conference: %s.", conference);
+ } else if (conference != null) {
+ String id = UUID.randomUUID().toString();
+ mConferenceById.put(id, conference);
+ mIdByConference.put(conference, id);
+ conference.addListener(mConferenceListener);
+ return id;
+ }
+
+ return null;
+ }
+
+ private void removeConference(Conference conference) {
+ if (mIdByConference.containsKey(conference)) {
+ conference.removeListener(mConferenceListener);
+
+ String id = mIdByConference.get(conference);
+ mConferenceById.remove(id);
+ mIdByConference.remove(conference);
+ mAdapter.removeCall(id);
+ }
+ }
+
+ private Connection findConnectionForAction(String callId, String action) {
+ if (mConnectionById.containsKey(callId)) {
+ return mConnectionById.get(callId);
+ }
+ Log.w(this, "%s - Cannot find Connection %s", action, callId);
+ return getNullConnection();
+ }
+
+ static synchronized Connection getNullConnection() {
+ if (sNullConnection == null) {
+ sNullConnection = new Connection() {};
+ }
+ return sNullConnection;
+ }
+
+ private Conference findConferenceForAction(String conferenceId, String action) {
+ if (mConferenceById.containsKey(conferenceId)) {
+ return mConferenceById.get(conferenceId);
+ }
+ Log.w(this, "%s - Cannot find conference %s", action, conferenceId);
+ return getNullConference();
+ }
+
+ private List<String> createConnectionIdList(List<Connection> connections) {
+ List<String> ids = new ArrayList<>();
+ for (Connection c : connections) {
+ if (mIdByConnection.containsKey(c)) {
+ ids.add(mIdByConnection.get(c));
+ }
+ }
+ Collections.sort(ids);
+ return ids;
+ }
+
+ private Conference getNullConference() {
+ if (sNullConference == null) {
+ sNullConference = new Conference(null) {};
+ }
+ return sNullConference;
+ }
+
+ private void endAllConnections() {
+ // Unbound from telecomm. We should end all connections and conferences.
+ for (Connection connection : mIdByConnection.keySet()) {
+ // only operate on top-level calls. Conference calls will be removed on their own.
+ if (connection.getConference() == null) {
+ connection.onDisconnect();
+ }
+ }
+ for (Conference conference : mIdByConference.keySet()) {
+ conference.onDisconnect();
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
new file mode 100644
index 0000000..c676172
--- /dev/null
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -0,0 +1,347 @@
+/*
+ * 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 android.net.Uri;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+
+import com.android.internal.telecom.IConnectionServiceAdapter;
+import com.android.internal.telecom.RemoteServiceCallback;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Provides methods for IConnectionService implementations to interact with the system phone app.
+ *
+ * @hide
+ */
+final class ConnectionServiceAdapter implements DeathRecipient {
+ /**
+ * 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<IConnectionServiceAdapter> mAdapters = Collections.newSetFromMap(
+ new ConcurrentHashMap<IConnectionServiceAdapter, Boolean>(8, 0.9f, 1));
+
+ ConnectionServiceAdapter() {
+ }
+
+ void addAdapter(IConnectionServiceAdapter adapter) {
+ if (mAdapters.add(adapter)) {
+ try {
+ adapter.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ mAdapters.remove(adapter);
+ }
+ }
+ }
+
+ void removeAdapter(IConnectionServiceAdapter adapter) {
+ if (adapter != null && mAdapters.remove(adapter)) {
+ adapter.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+
+ /** ${inheritDoc} */
+ @Override
+ public void binderDied() {
+ Iterator<IConnectionServiceAdapter> it = mAdapters.iterator();
+ while (it.hasNext()) {
+ IConnectionServiceAdapter adapter = it.next();
+ if (!adapter.asBinder().isBinderAlive()) {
+ it.remove();
+ adapter.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+ }
+
+ void handleCreateConnectionComplete(
+ String id,
+ ConnectionRequest request,
+ ParcelableConnection connection) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.handleCreateConnectionComplete(id, request, connection);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Sets a call's state to active (e.g., an ongoing call where two parties can actively
+ * communicate).
+ *
+ * @param callId The unique ID of the call whose state is changing to active.
+ */
+ void setActive(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setActive(callId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Sets a call's state to ringing (e.g., an inbound ringing call).
+ *
+ * @param callId The unique ID of the call whose state is changing to ringing.
+ */
+ void setRinging(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setRinging(callId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Sets a call's state to dialing (e.g., dialing an outbound call).
+ *
+ * @param callId The unique ID of the call whose state is changing to dialing.
+ */
+ void setDialing(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setDialing(callId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Sets a call's state to disconnected.
+ *
+ * @param callId The unique ID of the call whose state is changing to disconnected.
+ * @param disconnectCause The reason for the disconnection, as described by
+ * {@link android.telecomm.DisconnectCause}.
+ */
+ void setDisconnected(String callId, DisconnectCause disconnectCause) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setDisconnected(callId, disconnectCause);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Sets a call's state to be on hold.
+ *
+ * @param callId - The unique ID of the call whose state is changing to be on hold.
+ */
+ void setOnHold(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setOnHold(callId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Asks Telecom to start or stop a ringback tone for a call.
+ *
+ * @param callId The unique ID of the call whose ringback is being changed.
+ * @param ringback Whether Telecom should start playing a ringback tone.
+ */
+ void setRingbackRequested(String callId, boolean ringback) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setRingbackRequested(callId, ringback);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ void setCallCapabilities(String callId, int capabilities) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setCallCapabilities(callId, capabilities);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Indicates whether or not the specified call is currently conferenced into the specified
+ * conference call.
+ *
+ * @param callId The unique ID of the call being conferenced.
+ * @param conferenceCallId The unique ID of the conference call. Null if call is not
+ * conferenced.
+ */
+ void setIsConferenced(String callId, String conferenceCallId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ Log.d(this, "sending connection %s with conference %s", callId, conferenceCallId);
+ adapter.setIsConferenced(callId, conferenceCallId);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Indicates that the call no longer exists. Can be used with either a call or a conference
+ * call.
+ *
+ * @param callId The unique ID of the call.
+ */
+ void removeCall(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.removeCall(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ void onPostDialWait(String callId, String remaining) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.onPostDialWait(callId, remaining);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Indicates that a new conference call has been created.
+ *
+ * @param callId The unique ID of the conference call.
+ */
+ void addConferenceCall(String callId, ParcelableConference parcelableConference) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.addConferenceCall(callId, parcelableConference);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Retrieves a list of remote connection services usable to place calls.
+ */
+ void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+ // Only supported when there is only one adapter.
+ if (mAdapters.size() == 1) {
+ try {
+ mAdapters.iterator().next().queryRemoteConnectionServices(callback);
+ } catch (RemoteException e) {
+ Log.e(this, e, "Exception trying to query for remote CSs");
+ }
+ }
+ }
+
+ /**
+ * Sets the call video provider for a call.
+ *
+ * @param callId The unique ID of the call to set with the given call video provider.
+ * @param videoProvider The call video provider instance to set on the call.
+ */
+ void setVideoProvider(
+ String callId, Connection.VideoProvider videoProvider) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setVideoProvider(
+ callId,
+ videoProvider == null ? null : videoProvider.getInterface());
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Requests that the framework use VOIP audio mode for this connection.
+ *
+ * @param callId The unique ID of the call to set with the given call video provider.
+ * @param isVoip True if the audio mode is VOIP.
+ */
+ void setIsVoipAudioMode(String callId, boolean isVoip) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setIsVoipAudioMode(callId, isVoip);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ void setStatusHints(String callId, StatusHints statusHints) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setStatusHints(callId, statusHints);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ void setAddress(String callId, Uri address, int presentation) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setAddress(callId, address, presentation);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ void setCallerDisplayName(String callId, String callerDisplayName, int presentation) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setCallerDisplayName(callId, callerDisplayName, presentation);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Sets the video state associated with a call.
+ *
+ * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
+ * {@link VideoProfile.VideoState#BIDIRECTIONAL},
+ * {@link VideoProfile.VideoState#TX_ENABLED},
+ * {@link VideoProfile.VideoState#RX_ENABLED}.
+ *
+ * @param callId The unique ID of the call to set the video state for.
+ * @param videoState The video state.
+ */
+ void setVideoState(String callId, int videoState) {
+ Log.v(this, "setVideoState: %d", videoState);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setVideoState(callId, videoState);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ void setConferenceableConnections(String callId, List<String> conferenceableCallIds) {
+ Log.v(this, "setConferenceableConnections: %s, %s", callId, conferenceableCallIds);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setConferenceableConnections(callId, conferenceableCallIds);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
new file mode 100644
index 0000000..217dbc3
--- /dev/null
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -0,0 +1,357 @@
+/*
+ * 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
+ R* limitations under the License.
+ */
+
+package android.telecom;
+
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.IConnectionServiceAdapter;
+import com.android.internal.telecom.IVideoProvider;
+import com.android.internal.telecom.RemoteServiceCallback;
+
+import java.util.List;
+
+/**
+ * A component that provides an RPC servant implementation of {@link IConnectionServiceAdapter},
+ * posting incoming messages on the main thread on a client-supplied delegate object.
+ *
+ * TODO: Generate this and similar classes using a compiler starting from AIDL interfaces.
+ *
+ * @hide
+ */
+final class ConnectionServiceAdapterServant {
+ private static final int MSG_HANDLE_CREATE_CONNECTION_COMPLETE = 1;
+ private static final int MSG_SET_ACTIVE = 2;
+ private static final int MSG_SET_RINGING = 3;
+ private static final int MSG_SET_DIALING = 4;
+ private static final int MSG_SET_DISCONNECTED = 5;
+ private static final int MSG_SET_ON_HOLD = 6;
+ private static final int MSG_SET_RINGBACK_REQUESTED = 7;
+ private static final int MSG_SET_CALL_CAPABILITIES = 8;
+ private static final int MSG_SET_IS_CONFERENCED = 9;
+ private static final int MSG_ADD_CONFERENCE_CALL = 10;
+ private static final int MSG_REMOVE_CALL = 11;
+ private static final int MSG_ON_POST_DIAL_WAIT = 12;
+ private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 13;
+ private static final int MSG_SET_VIDEO_STATE = 14;
+ private static final int MSG_SET_VIDEO_CALL_PROVIDER = 15;
+ private static final int MSG_SET_IS_VOIP_AUDIO_MODE = 16;
+ private static final int MSG_SET_STATUS_HINTS = 17;
+ private static final int MSG_SET_ADDRESS = 18;
+ private static final int MSG_SET_CALLER_DISPLAY_NAME = 19;
+ private static final int MSG_SET_CONFERENCEABLE_CONNECTIONS = 20;
+
+ private final IConnectionServiceAdapter mDelegate;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ try {
+ internalHandleMessage(msg);
+ } catch (RemoteException e) {
+ }
+ }
+
+ // Internal method defined to centralize handling of RemoteException
+ private void internalHandleMessage(Message msg) throws RemoteException {
+ switch (msg.what) {
+ case MSG_HANDLE_CREATE_CONNECTION_COMPLETE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.handleCreateConnectionComplete(
+ (String) args.arg1,
+ (ConnectionRequest) args.arg2,
+ (ParcelableConnection) args.arg3);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_ACTIVE:
+ mDelegate.setActive((String) msg.obj);
+ break;
+ case MSG_SET_RINGING:
+ mDelegate.setRinging((String) msg.obj);
+ break;
+ case MSG_SET_DIALING:
+ mDelegate.setDialing((String) msg.obj);
+ break;
+ case MSG_SET_DISCONNECTED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setDisconnected((String) args.arg1, (DisconnectCause) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_ON_HOLD:
+ mDelegate.setOnHold((String) msg.obj);
+ break;
+ case MSG_SET_RINGBACK_REQUESTED:
+ mDelegate.setRingbackRequested((String) msg.obj, msg.arg1 == 1);
+ break;
+ case MSG_SET_CALL_CAPABILITIES:
+ mDelegate.setCallCapabilities((String) msg.obj, msg.arg1);
+ break;
+ case MSG_SET_IS_CONFERENCED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setIsConferenced((String) args.arg1, (String) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_ADD_CONFERENCE_CALL: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.addConferenceCall(
+ (String) args.arg1, (ParcelableConference) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_REMOVE_CALL:
+ mDelegate.removeCall((String) msg.obj);
+ break;
+ case MSG_ON_POST_DIAL_WAIT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.onPostDialWait((String) args.arg1, (String) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_QUERY_REMOTE_CALL_SERVICES:
+ mDelegate.queryRemoteConnectionServices((RemoteServiceCallback) msg.obj);
+ break;
+ case MSG_SET_VIDEO_STATE:
+ mDelegate.setVideoState((String) msg.obj, msg.arg1);
+ break;
+ case MSG_SET_VIDEO_CALL_PROVIDER: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setVideoProvider((String) args.arg1,
+ (IVideoProvider) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_IS_VOIP_AUDIO_MODE:
+ mDelegate.setIsVoipAudioMode((String) msg.obj, msg.arg1 == 1);
+ break;
+ case MSG_SET_STATUS_HINTS: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setStatusHints((String) args.arg1, (StatusHints) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_ADDRESS: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setAddress((String) args.arg1, (Uri) args.arg2, args.argi1);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_CALLER_DISPLAY_NAME: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setCallerDisplayName(
+ (String) args.arg1, (String) args.arg2, args.argi1);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_CONFERENCEABLE_CONNECTIONS: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setConferenceableConnections(
+ (String) args.arg1, (List<String>) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ }
+ }
+ };
+
+ private final IConnectionServiceAdapter mStub = new IConnectionServiceAdapter.Stub() {
+ @Override
+ public void handleCreateConnectionComplete(
+ String id,
+ ConnectionRequest request,
+ ParcelableConnection connection) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = id;
+ args.arg2 = request;
+ args.arg3 = connection;
+ mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_COMPLETE, args).sendToTarget();
+ }
+
+ @Override
+ public void setActive(String connectionId) {
+ mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget();
+ }
+
+ @Override
+ public void setRinging(String connectionId) {
+ mHandler.obtainMessage(MSG_SET_RINGING, connectionId).sendToTarget();
+ }
+
+ @Override
+ public void setDialing(String connectionId) {
+ mHandler.obtainMessage(MSG_SET_DIALING, connectionId).sendToTarget();
+ }
+
+ @Override
+ public void setDisconnected(
+ String connectionId, DisconnectCause disconnectCause) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.arg2 = disconnectCause;
+ mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
+ }
+
+ @Override
+ public void setOnHold(String connectionId) {
+ mHandler.obtainMessage(MSG_SET_ON_HOLD, connectionId).sendToTarget();
+ }
+
+ @Override
+ public void setRingbackRequested(String connectionId, boolean ringback) {
+ mHandler.obtainMessage(MSG_SET_RINGBACK_REQUESTED, ringback ? 1 : 0, 0, connectionId)
+ .sendToTarget();
+ }
+
+ @Override
+ public void setCallCapabilities(String connectionId, int callCapabilities) {
+ mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, callCapabilities, 0, connectionId)
+ .sendToTarget();
+ }
+
+ @Override
+ public void setIsConferenced(String callId, String conferenceCallId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = conferenceCallId;
+ mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget();
+ }
+
+ @Override
+ public void addConferenceCall(String callId, ParcelableConference parcelableConference) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = parcelableConference;
+ mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, args).sendToTarget();
+ }
+
+ @Override
+ public void removeCall(String connectionId) {
+ mHandler.obtainMessage(MSG_REMOVE_CALL, connectionId).sendToTarget();
+ }
+
+ @Override
+ public void onPostDialWait(String connectionId, String remainingDigits) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.arg2 = remainingDigits;
+ mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget();
+ }
+
+ @Override
+ public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+ mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
+ }
+
+ @Override
+ public void setVideoState(String connectionId, int videoState) {
+ mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, connectionId).sendToTarget();
+ }
+
+ @Override
+ public void setVideoProvider(String connectionId, IVideoProvider videoProvider) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.arg2 = videoProvider;
+ mHandler.obtainMessage(MSG_SET_VIDEO_CALL_PROVIDER, args).sendToTarget();
+ }
+
+ @Override
+ public final void setIsVoipAudioMode(String connectionId, boolean isVoip) {
+ mHandler.obtainMessage(MSG_SET_IS_VOIP_AUDIO_MODE, isVoip ? 1 : 0, 0,
+ connectionId).sendToTarget();
+ }
+
+ @Override
+ public final void setStatusHints(String connectionId, StatusHints statusHints) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.arg2 = statusHints;
+ mHandler.obtainMessage(MSG_SET_STATUS_HINTS, args).sendToTarget();
+ }
+
+ @Override
+ public final void setAddress(String connectionId, Uri address, int presentation) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.arg2 = address;
+ args.argi1 = presentation;
+ mHandler.obtainMessage(MSG_SET_ADDRESS, args).sendToTarget();
+ }
+
+ @Override
+ public final void setCallerDisplayName(
+ String connectionId, String callerDisplayName, int presentation) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.arg2 = callerDisplayName;
+ args.argi1 = presentation;
+ mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget();
+ }
+
+ @Override
+ public final void setConferenceableConnections(
+ String connectionId, List<String> conferenceableConnectionIds) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.arg2 = conferenceableConnectionIds;
+ mHandler.obtainMessage(MSG_SET_CONFERENCEABLE_CONNECTIONS, args).sendToTarget();
+ }
+ };
+
+ public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
+ mDelegate = delegate;
+ }
+
+ public IConnectionServiceAdapter getStub() {
+ return mStub;
+ }
+}
diff --git a/telecomm/java/android/telecom/DisconnectCause.aidl b/telecomm/java/android/telecom/DisconnectCause.aidl
new file mode 100644
index 0000000..26b8652
--- /dev/null
+++ b/telecomm/java/android/telecom/DisconnectCause.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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;
+
+/**
+ * {@hide}
+ */
+parcelable DisconnectCause;
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
new file mode 100644
index 0000000..cae115d
--- /dev/null
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -0,0 +1,249 @@
+/*
+ * 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 android.os.Parcel;
+import android.os.Parcelable;
+import android.media.ToneGenerator;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Describes the cause of a disconnected call. This always includes a code describing the generic
+ * cause of the disconnect. Optionally, it may include a localized label and/or localized description
+ * to display to the user which is provided by the {@link ConnectionService}. It also may contain a
+ * reason for the the disconnect, which is intended for logging and not for display to the user.
+ */
+public final class DisconnectCause implements Parcelable {
+
+ /** Disconnected because of an unknown or unspecified reason. */
+ public static final int UNKNOWN = 0;
+ /** Disconnected because there was an error, such as a problem with the network. */
+ public static final int ERROR = 1;
+ /** Disconnected because of a local user-initiated action, such as hanging up. */
+ public static final int LOCAL = 2;
+ /**
+ * Disconnected because of a remote user-initiated action, such as the other party hanging up
+ * up.
+ */
+ public static final int REMOTE = 3;
+ /** Disconnected because it has been canceled. */
+ public static final int CANCELED = 4;
+ /** Disconnected because there was no response to an incoming call. */
+ public static final int MISSED = 5;
+ /** Disconnected because the user rejected an incoming call. */
+ public static final int REJECTED = 6;
+ /** Disconnected because the other party was busy. */
+ public static final int BUSY = 7;
+ /**
+ * Disconnected because of a restriction on placing the call, such as dialing in airplane
+ * mode.
+ */
+ public static final int RESTRICTED = 8;
+ /** Disconnected for reason not described by other disconnect codes. */
+ public static final int OTHER = 9;
+
+ private int mDisconnectCode;
+ private CharSequence mDisconnectLabel;
+ private CharSequence mDisconnectDescription;
+ private String mDisconnectReason;
+ private int mToneToPlay;
+
+ /**
+ * Creates a new DisconnectCause.
+ *
+ * @param code The code for the disconnect cause.
+ */
+ public DisconnectCause(int code) {
+ this(code, null, null, null, ToneGenerator.TONE_UNKNOWN);
+ }
+
+ /**
+ * Creates a new DisconnectCause.
+ *
+ * @param code The code for the disconnect cause.
+ * @param reason The reason for the disconnect.
+ */
+ public DisconnectCause(int code, String reason) {
+ this(code, null, null, reason, ToneGenerator.TONE_UNKNOWN);
+ }
+
+ /**
+ * Creates a new DisconnectCause.
+ *
+ * @param code The code for the disconnect cause.
+ * @param label The localized label to show to the user to explain the disconnect.
+ * @param description The localized description to show to the user to explain the disconnect.
+ * @param reason The reason for the disconnect.
+ * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}.
+ */
+ public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
+ int toneToPlay) {
+ mDisconnectCode = code;
+ mDisconnectLabel = label;
+ mDisconnectDescription = description;
+ mDisconnectReason = reason;
+ mToneToPlay = toneToPlay;
+ }
+
+ /**
+ * Returns the code for the reason for this disconnect.
+ *
+ * @return The disconnect code.
+ */
+ public int getCode() {
+ return mDisconnectCode;
+ }
+
+ /**
+ * Returns a short label which explains the reason for the disconnect cause and is for display
+ * in the user interface. The {@link ConnectionService } is responsible for providing and
+ * localizing this label. If there is no string provided, returns null.
+ *
+ * @return The disconnect label.
+ */
+ public CharSequence getLabel() {
+ return mDisconnectLabel;
+ }
+
+ /**
+ * Returns a description which explains the reason for the disconnect cause and is for display
+ * in the user interface. The {@link ConnectionService } is responsible for providing and
+ * localizing this message. If there is no string provided, returns null.
+ *
+ * @return The disconnect description.
+ */
+ public CharSequence getDescription() {
+ return mDisconnectDescription;
+ }
+
+ /**
+ * Returns an explanation of the reason for the disconnect. This is not intended for display to
+ * the user and is used mainly for logging.
+ *
+ * @return The disconnect reason.
+ */
+ public String getReason() {
+ return mDisconnectReason;
+ }
+
+ /**
+ * Returns the tone to play when disconnected.
+ *
+ * @return the tone as defined in {@link ToneGenerator} to play when disconnected.
+ */
+ public int getTone() {
+ return mToneToPlay;
+ }
+
+ public static final Creator<DisconnectCause> CREATOR = new Creator<DisconnectCause>() {
+ @Override
+ public DisconnectCause createFromParcel(Parcel source) {
+ int code = source.readInt();
+ CharSequence label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ String reason = source.readString();
+ int tone = source.readInt();
+ return new DisconnectCause(code, label, description, reason, tone);
+ }
+
+ @Override
+ public DisconnectCause[] newArray(int size) {
+ return new DisconnectCause[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeInt(mDisconnectCode);
+ TextUtils.writeToParcel(mDisconnectLabel, destination, flags);
+ TextUtils.writeToParcel(mDisconnectDescription, destination, flags);
+ destination.writeString(mDisconnectReason);
+ destination.writeInt(mToneToPlay);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mDisconnectCode)
+ + Objects.hashCode(mDisconnectLabel)
+ + Objects.hashCode(mDisconnectDescription)
+ + Objects.hashCode(mDisconnectReason)
+ + Objects.hashCode(mToneToPlay);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof DisconnectCause) {
+ DisconnectCause d = (DisconnectCause) o;
+ return Objects.equals(mDisconnectCode, d.getCode())
+ && Objects.equals(mDisconnectLabel, d.getLabel())
+ && Objects.equals(mDisconnectDescription, d.getDescription())
+ && Objects.equals(mDisconnectReason, d.getReason())
+ && Objects.equals(mToneToPlay, d.getTone());
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ String code = "";
+ switch (getCode()) {
+ case ERROR:
+ code = "ERROR";
+ break;
+ case LOCAL:
+ code = "LOCAL";
+ break;
+ case REMOTE:
+ code = "REMOTE";
+ break;
+ case MISSED:
+ code = "MISSED";
+ break;
+ case REJECTED:
+ code = "REJECTED";
+ break;
+ case BUSY:
+ code = "BUSY";
+ break;
+ case RESTRICTED:
+ code = "RESTRICTED";
+ break;
+ case OTHER:
+ code = "OTHER";
+ break;
+ case UNKNOWN:
+ default:
+ code = "UNKNOWN";
+ }
+ String label = mDisconnectLabel == null ? "" : mDisconnectLabel.toString();
+ String description = mDisconnectDescription == null
+ ? "" : mDisconnectDescription.toString();
+ String reason = mDisconnectReason == null ? "" : mDisconnectReason;
+ return "DisconnectCause [ Code: (" + code + ")"
+ + " Label: (" + label + ")"
+ + " Description: (" + description + ")"
+ + " Reason: (" + reason + ")"
+ + " Tone: (" + mToneToPlay + ") ]";
+ }
+}
diff --git a/telecomm/java/android/telecom/GatewayInfo.aidl b/telecomm/java/android/telecom/GatewayInfo.aidl
new file mode 100644
index 0000000..ad9858c
--- /dev/null
+++ b/telecomm/java/android/telecom/GatewayInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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;
+
+/**
+ * {@hide}
+ */
+parcelable GatewayInfo;
diff --git a/telecomm/java/android/telecom/GatewayInfo.java b/telecomm/java/android/telecom/GatewayInfo.java
new file mode 100644
index 0000000..583c3e2
--- /dev/null
+++ b/telecomm/java/android/telecom/GatewayInfo.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 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 android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * When calls are made, they may contain gateway information for services which route phone calls
+ * through their own service/numbers. The data consists of a number to call and the package name of
+ * the service. This data is used in two ways:
+ * <ol>
+ * <li> Call the appropriate routing number
+ * <li> Display information about how the call is being routed to the user
+ * </ol>
+ */
+public class GatewayInfo implements Parcelable {
+
+ private final String mGatewayProviderPackageName;
+ private final Uri mGatewayAddress;
+ private final Uri mOriginalAddress;
+
+ /** @hide */
+ @SystemApi
+ public GatewayInfo(String packageName, Uri gatewayUri, Uri originalAddress) {
+ mGatewayProviderPackageName = packageName;
+ mGatewayAddress = gatewayUri;
+ mOriginalAddress = originalAddress;
+ }
+
+ /**
+ * Package name of the gateway provider service. used to place the call with.
+ */
+ public String getGatewayProviderPackageName() {
+ return mGatewayProviderPackageName;
+ }
+
+ /**
+ * Gateway provider address to use when actually placing the call.
+ */
+ public Uri getGatewayAddress() {
+ return mGatewayAddress;
+ }
+
+ /**
+ * The actual call address that the user is trying to connect to via the gateway.
+ */
+ public Uri getOriginalAddress() {
+ return mOriginalAddress;
+ }
+
+ public boolean isEmpty() {
+ return TextUtils.isEmpty(mGatewayProviderPackageName) || mGatewayAddress == null;
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Parcelable.Creator<GatewayInfo> CREATOR =
+ new Parcelable.Creator<GatewayInfo> () {
+
+ @Override
+ public GatewayInfo createFromParcel(Parcel source) {
+ String gatewayPackageName = source.readString();
+ Uri gatewayUri = Uri.CREATOR.createFromParcel(source);
+ Uri originalAddress = Uri.CREATOR.createFromParcel(source);
+ return new GatewayInfo(gatewayPackageName, gatewayUri, originalAddress);
+ }
+
+ @Override
+ public GatewayInfo[] newArray(int size) {
+ return new GatewayInfo[size];
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeString(mGatewayProviderPackageName);
+ mGatewayAddress.writeToParcel(destination, 0);
+ mOriginalAddress.writeToParcel(destination, 0);
+ }
+}
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
new file mode 100644
index 0000000..fd3cf2e
--- /dev/null
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -0,0 +1,274 @@
+/*
+ * 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 android.os.RemoteException;
+
+import com.android.internal.telecom.IInCallAdapter;
+
+/**
+ * Receives commands from {@link InCallService} implementations which should be executed by
+ * Telecom. When Telecom binds to a {@link InCallService}, an instance of this class is given to
+ * the in-call service through which it can manipulate live (active, dialing, ringing) calls. When
+ * the in-call service is notified of new calls, it can use the
+ * given call IDs to execute commands such as {@link #answerCall} for incoming calls or
+ * {@link #disconnectCall} for active calls the user would like to end. Some commands are only
+ * appropriate for calls in certain states; please consult each method for such limitations.
+ * <p>
+ * The adapter will stop functioning when there are no more calls.
+ *
+ * {@hide}
+ */
+public final class InCallAdapter {
+ private final IInCallAdapter mAdapter;
+
+ /**
+ * {@hide}
+ */
+ public InCallAdapter(IInCallAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ /**
+ * Instructs Telecom to answer the specified call.
+ *
+ * @param callId The identifier of the call to answer.
+ * @param videoState The video state in which to answer the call.
+ */
+ public void answerCall(String callId, int videoState) {
+ try {
+ mAdapter.answerCall(callId, videoState);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to reject the specified call.
+ *
+ * @param callId The identifier of the call to reject.
+ * @param rejectWithMessage Whether to reject with a text message.
+ * @param textMessage An optional text message with which to respond.
+ */
+ public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) {
+ try {
+ mAdapter.rejectCall(callId, rejectWithMessage, textMessage);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to disconnect the specified call.
+ *
+ * @param callId The identifier of the call to disconnect.
+ */
+ public void disconnectCall(String callId) {
+ try {
+ mAdapter.disconnectCall(callId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to put the specified call on hold.
+ *
+ * @param callId The identifier of the call to put on hold.
+ */
+ public void holdCall(String callId) {
+ try {
+ mAdapter.holdCall(callId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to release the specified call from hold.
+ *
+ * @param callId The identifier of the call to release from hold.
+ */
+ public void unholdCall(String callId) {
+ try {
+ mAdapter.unholdCall(callId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Mute the microphone.
+ *
+ * @param shouldMute True if the microphone should be muted.
+ */
+ public void mute(boolean shouldMute) {
+ try {
+ mAdapter.mute(shouldMute);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Sets the audio route (speaker, bluetooth, etc...). See {@link AudioState}.
+ *
+ * @param route The audio route to use.
+ */
+ public void setAudioRoute(int route) {
+ try {
+ mAdapter.setAudioRoute(route);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to play a dual-tone multi-frequency signaling (DTMF) tone in a call.
+ *
+ * Any other currently playing DTMF tone in the specified call is immediately stopped.
+ *
+ * @param callId The unique ID of the call in which the tone will be played.
+ * @param digit A character representing the DTMF digit for which to play the tone. This
+ * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
+ */
+ public void playDtmfTone(String callId, char digit) {
+ try {
+ mAdapter.playDtmfTone(callId, digit);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to stop any dual-tone multi-frequency signaling (DTMF) tone currently
+ * playing.
+ *
+ * DTMF tones are played by calling {@link #playDtmfTone(String,char)}. If no DTMF tone is
+ * currently playing, this method will do nothing.
+ *
+ * @param callId The unique ID of the call in which any currently playing tone will be stopped.
+ */
+ public void stopDtmfTone(String callId) {
+ try {
+ mAdapter.stopDtmfTone(callId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to continue playing a post-dial DTMF string.
+ *
+ * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
+ * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
+ * While these tones are playing, Telecom will notify the {@link InCallService} that the call
+ * is in the post dial state.
+ *
+ * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, Telecom
+ * will temporarily pause playing the tones for a pre-defined period of time.
+ *
+ * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, Telecom
+ * will pause playing the tones and notify the {@link InCallService} that the call is in the
+ * post dial wait state. When the user decides to continue the postdial sequence, the
+ * {@link InCallService} should invoke the {@link #postDialContinue(String,boolean)} method.
+ *
+ * @param callId The unique ID of the call for which postdial string playing should continue.
+ * @param proceed Whether or not to continue with the post-dial sequence.
+ */
+ public void postDialContinue(String callId, boolean proceed) {
+ try {
+ mAdapter.postDialContinue(callId, proceed);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to add a PhoneAccountHandle to the specified call
+ *
+ * @param callId The identifier of the call
+ * @param accountHandle The PhoneAccountHandle through which to place the call
+ */
+ public void phoneAccountSelected(String callId, PhoneAccountHandle accountHandle) {
+ try {
+ mAdapter.phoneAccountSelected(callId, accountHandle);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to conference the specified call.
+ *
+ * @param callId The unique ID of the call.
+ * @hide
+ */
+ public void conference(String callId, String otherCallId) {
+ try {
+ mAdapter.conference(callId, otherCallId);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to split the specified call from any conference call with which it may be
+ * connected.
+ *
+ * @param callId The unique ID of the call.
+ * @hide
+ */
+ public void splitFromConference(String callId) {
+ try {
+ mAdapter.splitFromConference(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to merge child calls of the specified conference call.
+ */
+ public void mergeConference(String callId) {
+ try {
+ mAdapter.mergeConference(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to swap the child calls of the specified conference call.
+ */
+ public void swapConference(String callId) {
+ try {
+ mAdapter.swapConference(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to turn the proximity sensor on.
+ */
+ public void turnProximitySensorOn() {
+ try {
+ mAdapter.turnOnProximitySensor();
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to turn the proximity sensor off.
+ *
+ * @param screenOnImmediately If true, the screen will be turned on immediately if it was
+ * previously off. Otherwise, the screen will only be turned on after the proximity sensor
+ * is no longer triggered.
+ */
+ public void turnProximitySensorOff(boolean screenOnImmediately) {
+ try {
+ mAdapter.turnOffProximitySensor(screenOnImmediately);
+ } catch (RemoteException ignored) {
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
new file mode 100644
index 0000000..fa12756
--- /dev/null
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2013 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 android.annotation.SystemApi;
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.view.Surface;
+
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.IInCallAdapter;
+import com.android.internal.telecom.IInCallService;
+
+import java.lang.String;
+
+/**
+ * This service is implemented by any app that wishes to provide the user-interface for managing
+ * phone calls. Telecom binds to this service while there exists a live (active or incoming) call,
+ * and uses it to notify the in-call app of any live and and recently disconnected calls.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class InCallService extends Service {
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telecom.InCallService";
+
+ private static final int MSG_SET_IN_CALL_ADAPTER = 1;
+ private static final int MSG_ADD_CALL = 2;
+ private static final int MSG_UPDATE_CALL = 3;
+ private static final int MSG_SET_POST_DIAL_WAIT = 4;
+ private static final int MSG_ON_AUDIO_STATE_CHANGED = 5;
+ private static final int MSG_BRING_TO_FOREGROUND = 6;
+
+ /** Default Handler used to consolidate binder method calls onto a single thread. */
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_IN_CALL_ADAPTER:
+ mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));
+ onPhoneCreated(mPhone);
+ break;
+ case MSG_ADD_CALL:
+ mPhone.internalAddCall((ParcelableCall) msg.obj);
+ break;
+ case MSG_UPDATE_CALL:
+ mPhone.internalUpdateCall((ParcelableCall) msg.obj);
+ break;
+ case MSG_SET_POST_DIAL_WAIT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ String remaining = (String) args.arg2;
+ mPhone.internalSetPostDialWait(callId, remaining);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_ON_AUDIO_STATE_CHANGED:
+ mPhone.internalAudioStateChanged((AudioState) msg.obj);
+ break;
+ case MSG_BRING_TO_FOREGROUND:
+ mPhone.internalBringToForeground(msg.arg1 == 1);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ /** Manages the binder calls so that the implementor does not need to deal with it. */
+ private final class InCallServiceBinder extends IInCallService.Stub {
+ @Override
+ public void setInCallAdapter(IInCallAdapter inCallAdapter) {
+ mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
+ }
+
+ @Override
+ public void addCall(ParcelableCall call) {
+ mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
+ }
+
+ @Override
+ public void updateCall(ParcelableCall call) {
+ mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();
+ }
+
+ @Override
+ public void setPostDial(String callId, String remaining) {
+ // TODO: Unused
+ }
+
+ @Override
+ public void setPostDialWait(String callId, String remaining) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = remaining;
+ mHandler.obtainMessage(MSG_SET_POST_DIAL_WAIT, args).sendToTarget();
+ }
+
+ @Override
+ public void onAudioStateChanged(AudioState audioState) {
+ mHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, audioState).sendToTarget();
+ }
+
+ @Override
+ public void bringToForeground(boolean showDialpad) {
+ mHandler.obtainMessage(MSG_BRING_TO_FOREGROUND, showDialpad ? 1 : 0, 0).sendToTarget();
+ }
+ }
+
+ private Phone mPhone;
+
+ public InCallService() {
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new InCallServiceBinder();
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ if (mPhone != null) {
+ Phone oldPhone = mPhone;
+ mPhone = null;
+
+ oldPhone.destroy();
+ onPhoneDestroyed(oldPhone);
+ }
+ return false;
+ }
+
+ /**
+ * Obtain the {@code Phone} associated with this {@code InCallService}.
+ *
+ * @return The {@code Phone} object associated with this {@code InCallService}, or {@code null}
+ * if the {@code InCallService} is not in a state where it has an associated
+ * {@code Phone}.
+ */
+ public Phone getPhone() {
+ return mPhone;
+ }
+
+ /**
+ * Invoked when the {@code Phone} has been created. This is a signal to the in-call experience
+ * to start displaying in-call information to the user. Each instance of {@code InCallService}
+ * will have only one {@code Phone}, and this method will be called exactly once in the lifetime
+ * of the {@code InCallService}.
+ *
+ * @param phone The {@code Phone} object associated with this {@code InCallService}.
+ */
+ public void onPhoneCreated(Phone phone) {
+ }
+
+ /**
+ * Invoked when a {@code Phone} has been destroyed. This is a signal to the in-call experience
+ * to stop displaying in-call information to the user. This method will be called exactly once
+ * in the lifetime of the {@code InCallService}, and it will always be called after a previous
+ * call to {@link #onPhoneCreated(Phone)}.
+ *
+ * @param phone The {@code Phone} object associated with this {@code InCallService}.
+ */
+ public void onPhoneDestroyed(Phone phone) {
+ }
+
+ /**
+ * Class to invoke functionality related to video calls.
+ * @hide
+ */
+ public static abstract class VideoCall {
+
+ /**
+ * Sets a listener to invoke callback methods in the InCallUI after performing video
+ * telephony actions.
+ *
+ * @param videoCallListener The call video client.
+ */
+ public abstract void setVideoCallListener(VideoCall.Listener videoCallListener);
+
+ /**
+ * Sets the camera to be used for video recording in a video call.
+ *
+ * @param cameraId The id of the camera.
+ */
+ public abstract void setCamera(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 setPreviewSurface(Surface surface);
+
+ /**
+ * Sets the surface to be used for displaying the video received from the remote device.
+ *
+ * @param surface The surface.
+ */
+ public abstract void setDisplaySurface(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 setDeviceOrientation(int rotation);
+
+ /**
+ * Sets camera zoom ratio.
+ *
+ * @param value The camera zoom ratio.
+ */
+ public abstract void setZoom(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
+ * {@link VideoCall.Listener#onSessionModifyRequestReceived}.
+ * 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 sendSessionModifyRequest(VideoProfile requestProfile);
+
+ /**
+ * 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
+ * {@link VideoCall.Listener#onSessionModifyRequestReceived}.
+ * The response is handled on the remove device by
+ * {@link VideoCall.Listener#onSessionModifyResponseReceived}.
+ *
+ * @param responseProfile The response call video properties.
+ */
+ public abstract void sendSessionModifyResponse(VideoProfile responseProfile);
+
+ /**
+ * Issues a request to the video provider to retrieve the camera capabilities.
+ * Camera capabilities are reported back to the caller via
+ * {@link VideoCall.Listener#onCameraCapabilitiesChanged(CameraCapabilities)}.
+ */
+ public abstract void requestCameraCapabilities();
+
+ /**
+ * 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
+ * {@link VideoCall.Listener#onCallDataUsageChanged}.
+ */
+ public abstract void requestCallDataUsage();
+
+ /**
+ * 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 setPauseImage(String uri);
+
+ /**
+ * Listener class which invokes callbacks after video call actions occur.
+ * @hide
+ */
+ public static abstract class Listener {
+ /**
+ * Called when a session modification request is received from the remote device.
+ * The remote request is sent via
+ * {@link Connection.VideoProvider#onSendSessionModifyRequest}. The InCall UI
+ * is responsible for potentially prompting the user whether they wish to accept the new
+ * call profile (e.g. prompt user if they wish to accept an upgrade from an audio to a
+ * video call) and should call
+ * {@link Connection.VideoProvider#onSendSessionModifyResponse} to indicate
+ * the video settings the user has agreed to.
+ *
+ * @param videoProfile The requested video call profile.
+ */
+ public abstract void onSessionModifyRequestReceived(VideoProfile videoProfile);
+
+ /**
+ * Called when a response to a session modification request is received from the remote
+ * device. The remote InCall UI sends the response using
+ * {@link Connection.VideoProvider#onSendSessionModifyResponse}.
+ *
+ * @param status Status of the session modify request. Valid values are
+ * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
+ * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
+ * {@link Connection.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 abstract void onSessionModifyResponseReceived(int status,
+ VideoProfile requestedProfile, VideoProfile responseProfile);
+
+ /**
+ * Handles events related to the current session which the client may wish to handle.
+ * These are separate from requested changes to the session due to the underlying
+ * protocol or connection.
+ *
+ * Valid values are:
+ * {@link Connection.VideoProvider#SESSION_EVENT_RX_PAUSE},
+ * {@link Connection.VideoProvider#SESSION_EVENT_RX_RESUME},
+ * {@link Connection.VideoProvider#SESSION_EVENT_TX_START},
+ * {@link Connection.VideoProvider#SESSION_EVENT_TX_STOP},
+ * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
+ * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_READY}
+ *
+ * @param event The event.
+ */
+ public abstract void onCallSessionEvent(int event);
+
+ /**
+ * Handles a change to the video dimensions from the remote caller (peer). This could
+ * happen if, for example, the peer changes orientation of their device.
+ *
+ * @param width The updated peer video width.
+ * @param height The updated peer video height.
+ */
+ public abstract void onPeerDimensionsChanged(int width, int height);
+
+ /**
+ * Handles an update to the total data used for the current session.
+ *
+ * @param dataUsage The updated data usage.
+ */
+ public abstract void onCallDataUsageChanged(int dataUsage);
+
+ /**
+ * Handles a change in camera capabilities.
+ *
+ * @param cameraCapabilities The changed camera capabilities.
+ */
+ public abstract void onCameraCapabilitiesChanged(
+ CameraCapabilities cameraCapabilities);
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
new file mode 100644
index 0000000..73cc4a5
--- /dev/null
+++ b/telecomm/java/android/telecom/Log.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 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 java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.IllegalFormatException;
+import java.util.Locale;
+
+/**
+ * Manages logging for the entire module.
+ *
+ * @hide
+ */
+final public class Log {
+
+ // Generic tag for all Telecom Framework logging
+ private static final String TAG = "TelecomFramework";
+
+ public static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
+ public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG);
+ public static final boolean INFO = isLoggable(android.util.Log.INFO);
+ public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
+ public static final boolean WARN = isLoggable(android.util.Log.WARN);
+ public static final boolean ERROR = isLoggable(android.util.Log.ERROR);
+
+ private Log() {}
+
+ public static boolean isLoggable(int level) {
+ return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
+ }
+
+ public static void d(String prefix, String format, Object... args) {
+ if (DEBUG) {
+ android.util.Log.d(TAG, buildMessage(prefix, format, args));
+ }
+ }
+
+ public static void d(Object objectPrefix, String format, Object... args) {
+ if (DEBUG) {
+ android.util.Log.d(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
+ }
+ }
+
+ public static void i(String prefix, String format, Object... args) {
+ if (INFO) {
+ android.util.Log.i(TAG, buildMessage(prefix, format, args));
+ }
+ }
+
+ public static void i(Object objectPrefix, String format, Object... args) {
+ if (INFO) {
+ android.util.Log.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
+ }
+ }
+
+ public static void v(String prefix, String format, Object... args) {
+ if (VERBOSE) {
+ android.util.Log.v(TAG, buildMessage(prefix, format, args));
+ }
+ }
+
+ public static void v(Object objectPrefix, String format, Object... args) {
+ if (VERBOSE) {
+ android.util.Log.v(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
+ }
+ }
+
+ public static void w(String prefix, String format, Object... args) {
+ if (WARN) {
+ android.util.Log.w(TAG, buildMessage(prefix, format, args));
+ }
+ }
+
+ public static void w(Object objectPrefix, String format, Object... args) {
+ if (WARN) {
+ android.util.Log.w(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
+ }
+ }
+
+ public static void e(String prefix, Throwable tr, String format, Object... args) {
+ if (ERROR) {
+ android.util.Log.e(TAG, buildMessage(prefix, format, args), tr);
+ }
+ }
+
+ public static void e(Object objectPrefix, Throwable tr, String format, Object... args) {
+ if (ERROR) {
+ android.util.Log.e(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
+ tr);
+ }
+ }
+
+ public static void wtf(String prefix, Throwable tr, String format, Object... args) {
+ android.util.Log.wtf(TAG, buildMessage(prefix, format, args), tr);
+ }
+
+ public static void wtf(Object objectPrefix, Throwable tr, String format, Object... args) {
+ android.util.Log.wtf(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
+ tr);
+ }
+
+ public static void wtf(String prefix, String format, Object... args) {
+ String msg = buildMessage(prefix, format, args);
+ android.util.Log.wtf(TAG, msg, new IllegalStateException(msg));
+ }
+
+ public static void wtf(Object objectPrefix, String format, Object... args) {
+ String msg = buildMessage(getPrefixFromObject(objectPrefix), format, args);
+ android.util.Log.wtf(TAG, msg, new IllegalStateException(msg));
+ }
+
+ /**
+ * Redact personally identifiable information for production users.
+ * If we are running in verbose mode, return the original string, otherwise
+ * return a SHA-1 hash of the input string.
+ */
+ public static String pii(Object pii) {
+ if (pii == null || VERBOSE) {
+ return String.valueOf(pii);
+ }
+ return "[" + secureHash(String.valueOf(pii).getBytes()) + "]";
+ }
+
+ private static String secureHash(byte[] input) {
+ MessageDigest messageDigest;
+ try {
+ messageDigest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+ messageDigest.update(input);
+ byte[] result = messageDigest.digest();
+ return encodeHex(result);
+ }
+
+ private static String encodeHex(byte[] bytes) {
+ StringBuffer hex = new StringBuffer(bytes.length * 2);
+
+ for (int i = 0; i < bytes.length; i++) {
+ int byteIntValue = bytes[i] & 0xff;
+ if (byteIntValue < 0x10) {
+ hex.append("0");
+ }
+ hex.append(Integer.toString(byteIntValue, 16));
+ }
+
+ return hex.toString();
+ }
+
+ private static String getPrefixFromObject(Object obj) {
+ return obj == null ? "<null>" : obj.getClass().getSimpleName();
+ }
+
+ private static String buildMessage(String prefix, String format, Object... args) {
+ String msg;
+ try {
+ msg = (args == null || args.length == 0) ? format
+ : String.format(Locale.US, format, args);
+ } catch (IllegalFormatException ife) {
+ wtf("Log", ife, "IllegalFormatException: formatString='%s' numArgs=%d", format,
+ args.length);
+ msg = format + " (An error occurred while formatting the message.)";
+ }
+ return String.format(Locale.US, "%s: %s", prefix, msg);
+ }
+}
diff --git a/telecomm/java/android/telecom/ParcelableCall.aidl b/telecomm/java/android/telecom/ParcelableCall.aidl
new file mode 100644
index 0000000..480e82f
--- /dev/null
+++ b/telecomm/java/android/telecom/ParcelableCall.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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;
+
+/**
+ * {@hide}
+ */
+parcelable ParcelableCall;
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
new file mode 100644
index 0000000..c5c3d11
--- /dev/null
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright 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 android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.android.internal.telecom.IVideoProvider;
+
+/**
+ * Information about a call that is used between InCallService and Telecom.
+ * @hide
+ */
+public final class ParcelableCall implements Parcelable {
+ private final String mId;
+ private final int mState;
+ private final DisconnectCause mDisconnectCause;
+ private final List<String> mCannedSmsResponses;
+ private final int mCapabilities;
+ private final int mProperties;
+ private final long mConnectTimeMillis;
+ private final Uri mHandle;
+ private final int mHandlePresentation;
+ private final String mCallerDisplayName;
+ private final int mCallerDisplayNamePresentation;
+ private final GatewayInfo mGatewayInfo;
+ private final PhoneAccountHandle mAccountHandle;
+ private final IVideoProvider mVideoCallProvider;
+ private InCallService.VideoCall mVideoCall;
+ private final String mParentCallId;
+ private final List<String> mChildCallIds;
+ private final StatusHints mStatusHints;
+ private final int mVideoState;
+ private final List<String> mConferenceableCallIds;
+ private final Bundle mExtras;
+
+ public ParcelableCall(
+ String id,
+ int state,
+ DisconnectCause disconnectCause,
+ List<String> cannedSmsResponses,
+ int capabilities,
+ int properties,
+ long connectTimeMillis,
+ Uri handle,
+ int handlePresentation,
+ String callerDisplayName,
+ int callerDisplayNamePresentation,
+ GatewayInfo gatewayInfo,
+ PhoneAccountHandle accountHandle,
+ IVideoProvider videoCallProvider,
+ String parentCallId,
+ List<String> childCallIds,
+ StatusHints statusHints,
+ int videoState,
+ List<String> conferenceableCallIds,
+ Bundle extras) {
+ mId = id;
+ mState = state;
+ mDisconnectCause = disconnectCause;
+ mCannedSmsResponses = cannedSmsResponses;
+ mCapabilities = capabilities;
+ mProperties = properties;
+ mConnectTimeMillis = connectTimeMillis;
+ mHandle = handle;
+ mHandlePresentation = handlePresentation;
+ mCallerDisplayName = callerDisplayName;
+ mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+ mGatewayInfo = gatewayInfo;
+ mAccountHandle = accountHandle;
+ mVideoCallProvider = videoCallProvider;
+ mParentCallId = parentCallId;
+ mChildCallIds = childCallIds;
+ mStatusHints = statusHints;
+ mVideoState = videoState;
+ mConferenceableCallIds = Collections.unmodifiableList(conferenceableCallIds);
+ mExtras = extras;
+ }
+
+ /** The unique ID of the call. */
+ public String getId() {
+ return mId;
+ }
+
+ /** The current state of the call. */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Reason for disconnection, as described by {@link android.telecomm.DisconnectCause}. Valid
+ * when call state is {@link CallState#DISCONNECTED}.
+ */
+ public DisconnectCause getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ /**
+ * The set of possible text message responses when this call is incoming.
+ */
+ public List<String> getCannedSmsResponses() {
+ return mCannedSmsResponses;
+ }
+
+ // Bit mask of actions a call supports, values are defined in {@link CallCapabilities}.
+ public int getCapabilities() {
+ return mCapabilities;
+ }
+
+ /** Bitmask of properties of the call. */
+ public int getProperties() { return mProperties; }
+
+ /** The time that the call switched to the active state. */
+ public long getConnectTimeMillis() {
+ return mConnectTimeMillis;
+ }
+
+ /** The endpoint to which the call is connected. */
+ public Uri getHandle() {
+ return mHandle;
+ }
+
+ /**
+ * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
+ */
+ public int getHandlePresentation() {
+ return mHandlePresentation;
+ }
+
+ /** The endpoint to which the call is connected. */
+ public String getCallerDisplayName() {
+ return mCallerDisplayName;
+ }
+
+ /**
+ * The presentation requirements for the caller display name.
+ * See {@link TelecomManager} for valid values.
+ */
+ public int getCallerDisplayNamePresentation() {
+ return mCallerDisplayNamePresentation;
+ }
+
+ /** Gateway information for the call. */
+ public GatewayInfo getGatewayInfo() {
+ return mGatewayInfo;
+ }
+
+ /** PhoneAccountHandle information for the call. */
+ public PhoneAccountHandle getAccountHandle() {
+ return mAccountHandle;
+ }
+
+ /**
+ * Returns an object for remotely communicating through the video call provider's binder.
+ * @return The video call.
+ */
+ public InCallService.VideoCall getVideoCall() {
+ if (mVideoCall == null && mVideoCallProvider != null) {
+ try {
+ mVideoCall = new VideoCallImpl(mVideoCallProvider);
+ } catch (RemoteException ignored) {
+ // Ignore RemoteException.
+ }
+ }
+
+ return mVideoCall;
+ }
+
+ /**
+ * The conference call to which this call is conferenced. Null if not conferenced.
+ */
+ public String getParentCallId() {
+ return mParentCallId;
+ }
+
+ /**
+ * The child call-IDs if this call is a conference call. Returns an empty list if this is not
+ * a conference call or if the conference call contains no children.
+ */
+ public List<String> getChildCallIds() {
+ return mChildCallIds;
+ }
+
+ public List<String> getConferenceableCallIds() {
+ return mConferenceableCallIds;
+ }
+
+ /**
+ * The status label and icon.
+ *
+ * @return Status hints.
+ */
+ public StatusHints getStatusHints() {
+ return mStatusHints;
+ }
+
+ /**
+ * The video state.
+ * @return The video state of the call.
+ */
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ /**
+ * Any extras to pass with the call
+ *
+ * @return a bundle of extras
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
+ public static final Parcelable.Creator<ParcelableCall> CREATOR =
+ new Parcelable.Creator<ParcelableCall> () {
+ @Override
+ public ParcelableCall createFromParcel(Parcel source) {
+ ClassLoader classLoader = ParcelableCall.class.getClassLoader();
+ String id = source.readString();
+ int state = source.readInt();
+ DisconnectCause disconnectCause = source.readParcelable(classLoader);
+ List<String> cannedSmsResponses = new ArrayList<>();
+ source.readList(cannedSmsResponses, classLoader);
+ int capabilities = source.readInt();
+ int properties = source.readInt();
+ long connectTimeMillis = source.readLong();
+ Uri handle = source.readParcelable(classLoader);
+ int handlePresentation = source.readInt();
+ String callerDisplayName = source.readString();
+ int callerDisplayNamePresentation = source.readInt();
+ GatewayInfo gatewayInfo = source.readParcelable(classLoader);
+ PhoneAccountHandle accountHandle = source.readParcelable(classLoader);
+ IVideoProvider videoCallProvider =
+ IVideoProvider.Stub.asInterface(source.readStrongBinder());
+ String parentCallId = source.readString();
+ List<String> childCallIds = new ArrayList<>();
+ source.readList(childCallIds, classLoader);
+ StatusHints statusHints = source.readParcelable(classLoader);
+ int videoState = source.readInt();
+ List<String> conferenceableCallIds = new ArrayList<>();
+ source.readList(conferenceableCallIds, classLoader);
+ Bundle extras = source.readParcelable(classLoader);
+ return new ParcelableCall(
+ id,
+ state,
+ disconnectCause,
+ cannedSmsResponses,
+ capabilities,
+ properties,
+ connectTimeMillis,
+ handle,
+ handlePresentation,
+ callerDisplayName,
+ callerDisplayNamePresentation,
+ gatewayInfo,
+ accountHandle,
+ videoCallProvider,
+ parentCallId,
+ childCallIds,
+ statusHints,
+ videoState,
+ conferenceableCallIds,
+ extras);
+ }
+
+ @Override
+ public ParcelableCall[] newArray(int size) {
+ return new ParcelableCall[size];
+ }
+ };
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Writes ParcelableCall object into a Parcel. */
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeString(mId);
+ destination.writeInt(mState);
+ destination.writeParcelable(mDisconnectCause, 0);
+ destination.writeList(mCannedSmsResponses);
+ destination.writeInt(mCapabilities);
+ destination.writeInt(mProperties);
+ destination.writeLong(mConnectTimeMillis);
+ destination.writeParcelable(mHandle, 0);
+ destination.writeInt(mHandlePresentation);
+ destination.writeString(mCallerDisplayName);
+ destination.writeInt(mCallerDisplayNamePresentation);
+ destination.writeParcelable(mGatewayInfo, 0);
+ destination.writeParcelable(mAccountHandle, 0);
+ destination.writeStrongBinder(
+ mVideoCallProvider != null ? mVideoCallProvider.asBinder() : null);
+ destination.writeString(mParentCallId);
+ destination.writeList(mChildCallIds);
+ destination.writeParcelable(mStatusHints, 0);
+ destination.writeInt(mVideoState);
+ destination.writeList(mConferenceableCallIds);
+ destination.writeParcelable(mExtras, 0);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[%s, parent:%s, children:%s]", mId, mParentCallId, mChildCallIds);
+ }
+}
diff --git a/telecomm/java/android/telecom/ParcelableConference.aidl b/telecomm/java/android/telecom/ParcelableConference.aidl
new file mode 100644
index 0000000..155ba94
--- /dev/null
+++ b/telecomm/java/android/telecom/ParcelableConference.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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;
+
+parcelable ParcelableConference;
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
new file mode 100644
index 0000000..97c709c
--- /dev/null
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 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 android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A parcelable representation of a conference connection.
+ * @hide
+ */
+public final class ParcelableConference implements Parcelable {
+
+ private PhoneAccountHandle mPhoneAccount;
+ private int mState;
+ private int mCapabilities;
+ private List<String> mConnectionIds;
+
+ public ParcelableConference(
+ PhoneAccountHandle phoneAccount,
+ int state,
+ int capabilities,
+ List<String> connectionIds) {
+ mPhoneAccount = phoneAccount;
+ mState = state;
+ mCapabilities = capabilities;
+ mConnectionIds = connectionIds;
+ }
+
+ @Override
+ public String toString() {
+ return (new StringBuffer())
+ .append("account: ")
+ .append(mPhoneAccount)
+ .append(", state: ")
+ .append(Connection.stateToString(mState))
+ .append(", capabilities: ")
+ .append(PhoneCapabilities.toString(mCapabilities))
+ .append(", children: ")
+ .append(mConnectionIds)
+ .toString();
+ }
+
+ public PhoneAccountHandle getPhoneAccount() {
+ return mPhoneAccount;
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ public int getCapabilities() {
+ return mCapabilities;
+ }
+
+ public List<String> getConnectionIds() {
+ return mConnectionIds;
+ }
+
+ public static final Parcelable.Creator<ParcelableConference> CREATOR =
+ new Parcelable.Creator<ParcelableConference> () {
+ @Override
+ public ParcelableConference createFromParcel(Parcel source) {
+ ClassLoader classLoader = ParcelableConference.class.getClassLoader();
+ PhoneAccountHandle phoneAccount = source.readParcelable(classLoader);
+ int state = source.readInt();
+ int capabilities = source.readInt();
+ List<String> connectionIds = new ArrayList<>(2);
+ source.readList(connectionIds, classLoader);
+
+ return new ParcelableConference(phoneAccount, state, capabilities, connectionIds);
+ }
+
+ @Override
+ public ParcelableConference[] newArray(int size) {
+ return new ParcelableConference[size];
+ }
+ };
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Writes ParcelableConference object into a Parcel. */
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeParcelable(mPhoneAccount, 0);
+ destination.writeInt(mState);
+ destination.writeInt(mCapabilities);
+ destination.writeList(mConnectionIds);
+ }
+}
diff --git a/telecomm/java/android/telecom/ParcelableConnection.aidl b/telecomm/java/android/telecom/ParcelableConnection.aidl
new file mode 100644
index 0000000..e91ebc3
--- /dev/null
+++ b/telecomm/java/android/telecom/ParcelableConnection.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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;
+
+/**
+ * {@hide}
+ */
+parcelable ParcelableConnection;
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
new file mode 100644
index 0000000..9004448
--- /dev/null
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 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 android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telecom.IVideoProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Information about a connection that is used between Telecom and the ConnectionService.
+ * This is used to send initial Connection information to Telecom when the connection is
+ * first created.
+ * @hide
+ */
+public final class ParcelableConnection implements Parcelable {
+ private final PhoneAccountHandle mPhoneAccount;
+ private final int mState;
+ private final int mCapabilities;
+ private final Uri mAddress;
+ private final int mAddressPresentation;
+ private final String mCallerDisplayName;
+ private final int mCallerDisplayNamePresentation;
+ private final IVideoProvider mVideoProvider;
+ private final int mVideoState;
+ private final boolean mRingbackRequested;
+ private final boolean mIsVoipAudioMode;
+ private final StatusHints mStatusHints;
+ private final DisconnectCause mDisconnectCause;
+ private final List<String> mConferenceableConnectionIds;
+
+ /** @hide */
+ public ParcelableConnection(
+ PhoneAccountHandle phoneAccount,
+ int state,
+ int capabilities,
+ Uri address,
+ int addressPresentation,
+ String callerDisplayName,
+ int callerDisplayNamePresentation,
+ IVideoProvider videoProvider,
+ int videoState,
+ boolean ringbackRequested,
+ boolean isVoipAudioMode,
+ StatusHints statusHints,
+ DisconnectCause disconnectCause,
+ List<String> conferenceableConnectionIds) {
+ mPhoneAccount = phoneAccount;
+ mState = state;
+ mCapabilities = capabilities;
+ mAddress = address;
+ mAddressPresentation = addressPresentation;
+ mCallerDisplayName = callerDisplayName;
+ mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+ mVideoProvider = videoProvider;
+ mVideoState = videoState;
+ mRingbackRequested = ringbackRequested;
+ mIsVoipAudioMode = isVoipAudioMode;
+ mStatusHints = statusHints;
+ mDisconnectCause = disconnectCause;
+ this.mConferenceableConnectionIds = conferenceableConnectionIds;
+ }
+
+ public PhoneAccountHandle getPhoneAccount() {
+ return mPhoneAccount;
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ // Bit mask of actions a call supports, values are defined in {@link CallCapabilities}.
+ public int getCapabilities() {
+ return mCapabilities;
+ }
+
+ public Uri getHandle() {
+ return mAddress;
+ }
+
+ public int getHandlePresentation() {
+ return mAddressPresentation;
+ }
+
+ public String getCallerDisplayName() {
+ return mCallerDisplayName;
+ }
+
+ public int getCallerDisplayNamePresentation() {
+ return mCallerDisplayNamePresentation;
+ }
+
+ public IVideoProvider getVideoProvider() {
+ return mVideoProvider;
+ }
+
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ public boolean isRingbackRequested() {
+ return mRingbackRequested;
+ }
+
+ public boolean getIsVoipAudioMode() {
+ return mIsVoipAudioMode;
+ }
+
+ public final StatusHints getStatusHints() {
+ return mStatusHints;
+ }
+
+ public final DisconnectCause getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ public final List<String> getConferenceableConnectionIds() {
+ return mConferenceableConnectionIds;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("ParcelableConnection [act:")
+ .append(mPhoneAccount)
+ .append(", state:")
+ .append(mState)
+ .append(", capabilities:")
+ .append(PhoneCapabilities.toString(mCapabilities))
+ .toString();
+ }
+
+ public static final Parcelable.Creator<ParcelableConnection> CREATOR =
+ new Parcelable.Creator<ParcelableConnection> () {
+ @Override
+ public ParcelableConnection createFromParcel(Parcel source) {
+ ClassLoader classLoader = ParcelableConnection.class.getClassLoader();
+
+ PhoneAccountHandle phoneAccount = source.readParcelable(classLoader);
+ int state = source.readInt();
+ int capabilities = source.readInt();
+ Uri address = source.readParcelable(classLoader);
+ int addressPresentation = source.readInt();
+ String callerDisplayName = source.readString();
+ int callerDisplayNamePresentation = source.readInt();
+ IVideoProvider videoCallProvider =
+ IVideoProvider.Stub.asInterface(source.readStrongBinder());
+ int videoState = source.readInt();
+ boolean ringbackRequested = source.readByte() == 1;
+ boolean audioModeIsVoip = source.readByte() == 1;
+ StatusHints statusHints = source.readParcelable(classLoader);
+ DisconnectCause disconnectCause = source.readParcelable(classLoader);
+ List<String> conferenceableConnectionIds = new ArrayList<>();
+ source.readStringList(conferenceableConnectionIds);
+
+ return new ParcelableConnection(
+ phoneAccount,
+ state,
+ capabilities,
+ address,
+ addressPresentation,
+ callerDisplayName,
+ callerDisplayNamePresentation,
+ videoCallProvider,
+ videoState,
+ ringbackRequested,
+ audioModeIsVoip,
+ statusHints,
+ disconnectCause,
+ conferenceableConnectionIds);
+ }
+
+ @Override
+ public ParcelableConnection[] newArray(int size) {
+ return new ParcelableConnection[size];
+ }
+ };
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Writes ParcelableConnection object into a Parcel. */
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeParcelable(mPhoneAccount, 0);
+ destination.writeInt(mState);
+ destination.writeInt(mCapabilities);
+ destination.writeParcelable(mAddress, 0);
+ destination.writeInt(mAddressPresentation);
+ destination.writeString(mCallerDisplayName);
+ destination.writeInt(mCallerDisplayNamePresentation);
+ destination.writeStrongBinder(
+ mVideoProvider != null ? mVideoProvider.asBinder() : null);
+ destination.writeInt(mVideoState);
+ destination.writeByte((byte) (mRingbackRequested ? 1 : 0));
+ destination.writeByte((byte) (mIsVoipAudioMode ? 1 : 0));
+ destination.writeParcelable(mStatusHints, 0);
+ destination.writeParcelable(mDisconnectCause, 0);
+ destination.writeStringList(mConferenceableConnectionIds);
+ }
+}
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
new file mode 100644
index 0000000..5131790
--- /dev/null
+++ b/telecomm/java/android/telecom/Phone.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2013 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 android.annotation.SystemApi;
+import android.util.ArrayMap;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * A unified virtual device providing a means of voice (and other) communication on a device.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class Phone {
+
+ public abstract static class Listener {
+ /**
+ * Called when the audio state changes.
+ *
+ * @param phone The {@code Phone} calling this method.
+ * @param audioState The new {@link AudioState}.
+ */
+ public void onAudioStateChanged(Phone phone, AudioState audioState) { }
+
+ /**
+ * Called to bring the in-call screen to the foreground. The in-call experience should
+ * respond immediately by coming to the foreground to inform the user of the state of
+ * ongoing {@code Call}s.
+ *
+ * @param phone The {@code Phone} calling this method.
+ * @param showDialpad If true, put up the dialpad when the screen is shown.
+ */
+ public void onBringToForeground(Phone phone, boolean showDialpad) { }
+
+ /**
+ * Called when a {@code Call} has been added to this in-call session. The in-call user
+ * experience should add necessary state listeners to the specified {@code Call} and
+ * immediately start to show the user information about the existence
+ * and nature of this {@code Call}. Subsequent invocations of {@link #getCalls()} will
+ * include this {@code Call}.
+ *
+ * @param phone The {@code Phone} calling this method.
+ * @param call A newly added {@code Call}.
+ */
+ public void onCallAdded(Phone phone, Call call) { }
+
+ /**
+ * Called when a {@code Call} has been removed from this in-call session. The in-call user
+ * experience should remove any state listeners from the specified {@code Call} and
+ * immediately stop displaying any information about this {@code Call}.
+ * Subsequent invocations of {@link #getCalls()} will no longer include this {@code Call}.
+ *
+ * @param phone The {@code Phone} calling this method.
+ * @param call A newly removed {@code Call}.
+ */
+ public void onCallRemoved(Phone phone, Call call) { }
+ }
+
+ // A Map allows us to track each Call by its Telecom-specified call ID
+ private final Map<String, Call> mCallByTelecomCallId = new ArrayMap<>();
+
+ // A List allows us to keep the Calls in a stable iteration order so that casually developed
+ // user interface components do not incur any spurious jank
+ private final List<Call> mCalls = new CopyOnWriteArrayList<>();
+
+ // An unmodifiable view of the above List can be safely shared with subclass implementations
+ private final List<Call> mUnmodifiableCalls = Collections.unmodifiableList(mCalls);
+
+ private final InCallAdapter mInCallAdapter;
+
+ private AudioState mAudioState;
+
+ private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
+
+ /** {@hide} */
+ Phone(InCallAdapter adapter) {
+ mInCallAdapter = adapter;
+ }
+
+ /** {@hide} */
+ final void internalAddCall(ParcelableCall parcelableCall) {
+ Call call = new Call(this, parcelableCall.getId(), mInCallAdapter);
+ mCallByTelecomCallId.put(parcelableCall.getId(), call);
+ mCalls.add(call);
+ checkCallTree(parcelableCall);
+ call.internalUpdate(parcelableCall, mCallByTelecomCallId);
+ fireCallAdded(call);
+ }
+
+ /** {@hide} */
+ final void internalRemoveCall(Call call) {
+ mCallByTelecomCallId.remove(call.internalGetCallId());
+ mCalls.remove(call);
+ fireCallRemoved(call);
+ }
+
+ /** {@hide} */
+ final void internalUpdateCall(ParcelableCall parcelableCall) {
+ Call call = mCallByTelecomCallId.get(parcelableCall.getId());
+ if (call != null) {
+ checkCallTree(parcelableCall);
+ call.internalUpdate(parcelableCall, mCallByTelecomCallId);
+ }
+ }
+
+ /** {@hide} */
+ final void internalSetPostDialWait(String telecomId, String remaining) {
+ Call call = mCallByTelecomCallId.get(telecomId);
+ if (call != null) {
+ call.internalSetPostDialWait(remaining);
+ }
+ }
+
+ /** {@hide} */
+ final void internalAudioStateChanged(AudioState audioState) {
+ if (!Objects.equals(mAudioState, audioState)) {
+ mAudioState = audioState;
+ fireAudioStateChanged(audioState);
+ }
+ }
+
+ /** {@hide} */
+ final Call internalGetCallByTelecomId(String telecomId) {
+ return mCallByTelecomCallId.get(telecomId);
+ }
+
+ /** {@hide} */
+ final void internalBringToForeground(boolean showDialpad) {
+ fireBringToForeground(showDialpad);
+ }
+
+ /**
+ * Called to destroy the phone and cleanup any lingering calls.
+ * @hide
+ */
+ final void destroy() {
+ for (Call call : mCalls) {
+ if (call.getState() != Call.STATE_DISCONNECTED) {
+ call.internalSetDisconnected();
+ }
+ }
+ }
+
+ /**
+ * Adds a listener to this {@code Phone}.
+ *
+ * @param listener A {@code Listener} object.
+ */
+ public final void addListener(Listener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener from this {@code Phone}.
+ *
+ * @param listener A {@code Listener} object.
+ */
+ public final void removeListener(Listener listener) {
+ if (listener != null) {
+ mListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Obtains the current list of {@code Call}s to be displayed by this in-call experience.
+ *
+ * @return A list of the relevant {@code Call}s.
+ */
+ public final List<Call> getCalls() {
+ return mUnmodifiableCalls;
+ }
+
+ /**
+ * Sets the microphone mute state. When this request is honored, there will be change to
+ * the {@link #getAudioState()}.
+ *
+ * @param state {@code true} if the microphone should be muted; {@code false} otherwise.
+ */
+ public final void setMuted(boolean state) {
+ mInCallAdapter.mute(state);
+ }
+
+ /**
+ * Sets the audio route (speaker, bluetooth, etc...). When this request is honored, there will
+ * be change to the {@link #getAudioState()}.
+ *
+ * @param route The audio route to use.
+ */
+ public final void setAudioRoute(int route) {
+ mInCallAdapter.setAudioRoute(route);
+ }
+
+ /**
+ * Turns the proximity sensor on. When this request is made, the proximity sensor will
+ * become active, and the touch screen and display will be turned off when the user's face
+ * is detected to be in close proximity to the screen. This operation is a no-op on devices
+ * that do not have a proximity sensor.
+ */
+ public final void setProximitySensorOn() {
+ mInCallAdapter.turnProximitySensorOn();
+ }
+
+ /**
+ * Turns the proximity sensor off. When this request is made, the proximity sensor will
+ * become inactive, and no longer affect the touch screen and display. This operation is a
+ * no-op on devices that do not have a proximity sensor.
+ *
+ * @param screenOnImmediately If true, the screen will be turned on immediately if it was
+ * previously off. Otherwise, the screen will only be turned on after the proximity sensor
+ * is no longer triggered.
+ */
+ public final void setProximitySensorOff(boolean screenOnImmediately) {
+ mInCallAdapter.turnProximitySensorOff(screenOnImmediately);
+ }
+
+ /**
+ * Obtains the current phone call audio state of the {@code Phone}.
+ *
+ * @return An object encapsulating the audio state.
+ */
+ public final AudioState getAudioState() {
+ return mAudioState;
+ }
+
+ private void fireCallAdded(Call call) {
+ for (Listener listener : mListeners) {
+ listener.onCallAdded(this, call);
+ }
+ }
+
+ private void fireCallRemoved(Call call) {
+ for (Listener listener : mListeners) {
+ listener.onCallRemoved(this, call);
+ }
+ }
+
+ private void fireAudioStateChanged(AudioState audioState) {
+ for (Listener listener : mListeners) {
+ listener.onAudioStateChanged(this, audioState);
+ }
+ }
+
+ private void fireBringToForeground(boolean showDialpad) {
+ for (Listener listener : mListeners) {
+ listener.onBringToForeground(this, showDialpad);
+ }
+ }
+
+ private void checkCallTree(ParcelableCall parcelableCall) {
+ if (parcelableCall.getParentCallId() != null &&
+ !mCallByTelecomCallId.containsKey(parcelableCall.getParentCallId())) {
+ Log.wtf(this, "ParcelableCall %s has nonexistent parent %s",
+ parcelableCall.getId(), parcelableCall.getParentCallId());
+ }
+ if (parcelableCall.getChildCallIds() != null) {
+ for (int i = 0; i < parcelableCall.getChildCallIds().size(); i++) {
+ if (!mCallByTelecomCallId.containsKey(parcelableCall.getChildCallIds().get(i))) {
+ Log.wtf(this, "ParcelableCall %s has nonexistent child %s",
+ parcelableCall.getId(), parcelableCall.getChildCallIds().get(i));
+ }
+ }
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/PhoneAccount.aidl b/telecomm/java/android/telecom/PhoneAccount.aidl
new file mode 100644
index 0000000..d5e6058
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccount.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+parcelable PhoneAccount;
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
new file mode 100644
index 0000000..4b059b24
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -0,0 +1,449 @@
+/*
+ * 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 android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.MissingResourceException;
+
+/**
+ * Describes a distinct account, line of service or call placement method that the system
+ * can use to place phone calls.
+ */
+public class PhoneAccount implements Parcelable {
+
+ /**
+ * Flag indicating that this {@code PhoneAccount} can act as a connection manager for
+ * other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
+ * will be allowed to manage phone calls including using its own proprietary phone-call
+ * implementation (like VoIP calling) to make calls instead of the telephony stack.
+ * <p>
+ * When a user opts to place a call using the SIM-based telephony stack, the
+ * {@link ConnectionService} associated with this {@code PhoneAccount} will be attempted first
+ * if the user has explicitly selected it to be used as the default connection manager.
+ * <p>
+ * See {@link #getCapabilities}
+ */
+ public static final int CAPABILITY_CONNECTION_MANAGER = 0x1;
+
+ /**
+ * Flag indicating that this {@code PhoneAccount} can make phone calls in place of
+ * traditional SIM-based telephony calls. This account will be treated as a distinct method
+ * for placing calls alongside the traditional SIM-based telephony stack. This flag is
+ * distinct from {@link #CAPABILITY_CONNECTION_MANAGER} in that it is not allowed to manage
+ * calls from or use the built-in telephony stack to place its calls.
+ * <p>
+ * See {@link #getCapabilities}
+ * <p>
+ * {@hide}
+ */
+ public static final int CAPABILITY_CALL_PROVIDER = 0x2;
+
+ /**
+ * Flag indicating that this {@code PhoneAccount} represents a built-in PSTN SIM
+ * subscription.
+ * <p>
+ * Only the Android framework can register a {@code PhoneAccount} having this capability.
+ * <p>
+ * See {@link #getCapabilities}
+ */
+ public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4;
+
+ /**
+ * Flag indicating that this {@code PhoneAccount} is capable of placing video calls.
+ * <p>
+ * See {@link #getCapabilities}
+ * @hide
+ */
+ public static final int CAPABILITY_VIDEO_CALLING = 0x8;
+
+ /**
+ * Flag indicating that this {@code PhoneAccount} is capable of placing emergency calls.
+ * By default all PSTN {@code PhoneAccount}s are capable of placing emergency calls.
+ * <p>
+ * See {@link #getCapabilities}
+ */
+ public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 0x10;
+
+ /**
+ * URI scheme for telephone number URIs.
+ */
+ public static final String SCHEME_TEL = "tel";
+
+ /**
+ * URI scheme for voicemail URIs.
+ */
+ public static final String SCHEME_VOICEMAIL = "voicemail";
+
+ /**
+ * URI scheme for SIP URIs.
+ */
+ public static final String SCHEME_SIP = "sip";
+
+ private final PhoneAccountHandle mAccountHandle;
+ private final Uri mAddress;
+ private final Uri mSubscriptionAddress;
+ private final int mCapabilities;
+ private final int mIconResId;
+ private final CharSequence mLabel;
+ private final CharSequence mShortDescription;
+ private final List<String> mSupportedUriSchemes;
+
+ public static class Builder {
+ private PhoneAccountHandle mAccountHandle;
+ private Uri mAddress;
+ private Uri mSubscriptionAddress;
+ private int mCapabilities;
+ private int mIconResId;
+ private CharSequence mLabel;
+ private CharSequence mShortDescription;
+ private List<String> mSupportedUriSchemes = new ArrayList<String>();
+
+ public Builder(PhoneAccountHandle accountHandle, CharSequence label) {
+ this.mAccountHandle = accountHandle;
+ this.mLabel = label;
+ }
+
+ /**
+ * Creates an instance of the {@link PhoneAccount.Builder} from an existing
+ * {@link PhoneAccount}.
+ *
+ * @param phoneAccount The {@link PhoneAccount} used to initialize the builder.
+ */
+ public Builder(PhoneAccount phoneAccount) {
+ mAccountHandle = phoneAccount.getAccountHandle();
+ mAddress = phoneAccount.getAddress();
+ mSubscriptionAddress = phoneAccount.getSubscriptionAddress();
+ mCapabilities = phoneAccount.getCapabilities();
+ mIconResId = phoneAccount.getIconResId();
+ mLabel = phoneAccount.getLabel();
+ mShortDescription = phoneAccount.getShortDescription();
+ mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
+ }
+
+ public Builder setAddress(Uri value) {
+ this.mAddress = value;
+ return this;
+ }
+
+ public Builder setSubscriptionAddress(Uri value) {
+ this.mSubscriptionAddress = value;
+ return this;
+ }
+
+ public Builder setCapabilities(int value) {
+ this.mCapabilities = value;
+ return this;
+ }
+
+ public Builder setIconResId(int value) {
+ this.mIconResId = value;
+ return this;
+ }
+
+ public Builder setShortDescription(CharSequence value) {
+ this.mShortDescription = value;
+ return this;
+ }
+
+ /**
+ * Specifies an additional URI scheme supported by the {@link PhoneAccount}.
+ *
+ * @param uriScheme The URI scheme.
+ * @return The Builder.
+ * @hide
+ */
+ public Builder addSupportedUriScheme(String uriScheme) {
+ if (!TextUtils.isEmpty(uriScheme) && !mSupportedUriSchemes.contains(uriScheme)) {
+ this.mSupportedUriSchemes.add(uriScheme);
+ }
+ return this;
+ }
+
+ /**
+ * Specifies the URI schemes supported by the {@link PhoneAccount}.
+ *
+ * @param uriSchemes The URI schemes.
+ * @return The Builder.
+ */
+ public Builder setSupportedUriSchemes(List<String> uriSchemes) {
+ mSupportedUriSchemes.clear();
+
+ if (uriSchemes != null && !uriSchemes.isEmpty()) {
+ for (String uriScheme : uriSchemes) {
+ addSupportedUriScheme(uriScheme);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Creates an instance of a {@link PhoneAccount} based on the current builder settings.
+ *
+ * @return The {@link PhoneAccount}.
+ */
+ public PhoneAccount build() {
+ // If no supported URI schemes were defined, assume "tel" is supported.
+ if (mSupportedUriSchemes.isEmpty()) {
+ addSupportedUriScheme(SCHEME_TEL);
+ }
+
+ return new PhoneAccount(
+ mAccountHandle,
+ mAddress,
+ mSubscriptionAddress,
+ mCapabilities,
+ mIconResId,
+ mLabel,
+ mShortDescription,
+ mSupportedUriSchemes);
+ }
+ }
+
+ private PhoneAccount(
+ PhoneAccountHandle account,
+ Uri address,
+ Uri subscriptionAddress,
+ int capabilities,
+ int iconResId,
+ CharSequence label,
+ CharSequence shortDescription,
+ List<String> supportedUriSchemes) {
+ mAccountHandle = account;
+ mAddress = address;
+ mSubscriptionAddress = subscriptionAddress;
+ mCapabilities = capabilities;
+ mIconResId = iconResId;
+ mLabel = label;
+ mShortDescription = shortDescription;
+ mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
+ }
+
+ public static Builder builder(
+ PhoneAccountHandle accountHandle,
+ CharSequence label) {
+ return new Builder(accountHandle, label);
+ }
+
+ /**
+ * Returns a builder initialized with the current {@link PhoneAccount} instance.
+ *
+ * @return The builder.
+ * @hide
+ */
+ public Builder toBuilder() { return new Builder(this); }
+
+ /**
+ * The unique identifier of this {@code PhoneAccount}.
+ *
+ * @return A {@code PhoneAccountHandle}.
+ */
+ public PhoneAccountHandle getAccountHandle() {
+ return mAccountHandle;
+ }
+
+ /**
+ * The address (e.g., a phone number) associated with this {@code PhoneAccount}. This
+ * represents the destination from which outgoing calls using this {@code PhoneAccount}
+ * will appear to come, if applicable, and the destination to which incoming calls using this
+ * {@code PhoneAccount} may be addressed.
+ *
+ * @return A address expressed as a {@code Uri}, for example, a phone number.
+ */
+ public Uri getAddress() {
+ return mAddress;
+ }
+
+ /**
+ * The raw callback number used for this {@code PhoneAccount}, as distinct from
+ * {@link #getAddress()}. For the majority of {@code PhoneAccount}s this should be registered
+ * as {@code null}. It is used by the system for SIM-based {@code PhoneAccount} registration
+ * where {@link android.telephony.TelephonyManager#setLine1NumberForDisplay(String, String)}
+ * has been used to alter the callback number.
+ * <p>
+ *
+ * @return The subscription number, suitable for display to the user.
+ */
+ public Uri getSubscriptionAddress() {
+ return mSubscriptionAddress;
+ }
+
+ /**
+ * The capabilities of this {@code PhoneAccount}.
+ *
+ * @return A bit field of flags describing this {@code PhoneAccount}'s capabilities.
+ */
+ public int getCapabilities() {
+ return mCapabilities;
+ }
+
+ /**
+ * Determines if this {@code PhoneAccount} has a capabilities specified by the passed in
+ * bit mask.
+ *
+ * @param capability The capabilities to check.
+ * @return {@code True} if the phone account has the capability.
+ */
+ public boolean hasCapabilities(int capability) {
+ return (mCapabilities & capability) == capability;
+ }
+
+ /**
+ * A short label describing a {@code PhoneAccount}.
+ *
+ * @return A label for this {@code PhoneAccount}.
+ */
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * A short paragraph describing this {@code PhoneAccount}.
+ *
+ * @return A description for this {@code PhoneAccount}.
+ */
+ public CharSequence getShortDescription() {
+ return mShortDescription;
+ }
+
+ /**
+ * The URI schemes supported by this {@code PhoneAccount}.
+ *
+ * @return The URI schemes.
+ */
+ public List<String> getSupportedUriSchemes() {
+ return mSupportedUriSchemes;
+ }
+
+ /**
+ * Determines if the {@link PhoneAccount} supports calls to/from addresses with a specified URI
+ * scheme.
+ *
+ * @param uriScheme The URI scheme to check.
+ * @return {@code True} if the {@code PhoneAccount} supports calls to/from addresses with the
+ * specified URI scheme.
+ */
+ public boolean supportsUriScheme(String uriScheme) {
+ if (mSupportedUriSchemes == null || uriScheme == null) {
+ return false;
+ }
+
+ for (String scheme : mSupportedUriSchemes) {
+ if (scheme != null && scheme.equals(uriScheme)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * The icon resource ID for the icon of this {@code PhoneAccount}.
+ *
+ * @return A resource ID.
+ */
+ public int getIconResId() {
+ return mIconResId;
+ }
+
+ /**
+ * An icon to represent this {@code PhoneAccount} in a user interface.
+ *
+ * @return An icon for this {@code PhoneAccount}.
+ */
+ public Drawable getIcon(Context context) {
+ return getIcon(context, mIconResId);
+ }
+
+ private Drawable getIcon(Context context, int resId) {
+ Context packageContext;
+ try {
+ packageContext = context.createPackageContext(
+ mAccountHandle.getComponentName().getPackageName(), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(this, "Cannot find package %s", mAccountHandle.getComponentName().getPackageName());
+ return null;
+ }
+ try {
+ return packageContext.getDrawable(resId);
+ } catch (NotFoundException|MissingResourceException e) {
+ Log.e(this, e, "Cannot find icon %d in package %s",
+ resId, mAccountHandle.getComponentName().getPackageName());
+ return null;
+ }
+ }
+
+ //
+ // Parcelable implementation
+ //
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(mAccountHandle, 0);
+ out.writeParcelable(mAddress, 0);
+ out.writeParcelable(mSubscriptionAddress, 0);
+ out.writeInt(mCapabilities);
+ out.writeInt(mIconResId);
+ out.writeCharSequence(mLabel);
+ out.writeCharSequence(mShortDescription);
+ out.writeList(mSupportedUriSchemes);
+ }
+
+ public static final Creator<PhoneAccount> CREATOR
+ = new Creator<PhoneAccount>() {
+ @Override
+ public PhoneAccount createFromParcel(Parcel in) {
+ return new PhoneAccount(in);
+ }
+
+ @Override
+ public PhoneAccount[] newArray(int size) {
+ return new PhoneAccount[size];
+ }
+ };
+
+ private PhoneAccount(Parcel in) {
+ ClassLoader classLoader = PhoneAccount.class.getClassLoader();
+
+ mAccountHandle = in.readParcelable(getClass().getClassLoader());
+ mAddress = in.readParcelable(getClass().getClassLoader());
+ mSubscriptionAddress = in.readParcelable(getClass().getClassLoader());
+ mCapabilities = in.readInt();
+ mIconResId = in.readInt();
+ mLabel = in.readCharSequence();
+ mShortDescription = in.readCharSequence();
+
+ List<String> supportedUriSchemes = new ArrayList<>();
+ in.readList(supportedUriSchemes, classLoader);
+ mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
+ }
+}
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.aidl b/telecomm/java/android/telecom/PhoneAccountHandle.aidl
new file mode 100644
index 0000000..f8f9656
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2008 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;
+
+/**
+ * {@hide}
+ */
+parcelable PhoneAccountHandle;
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
new file mode 100644
index 0000000..e13df76
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -0,0 +1,120 @@
+/*
+ * 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 android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The unique identifier for a {@link PhoneAccount}.
+ */
+public class PhoneAccountHandle implements Parcelable {
+ private ComponentName mComponentName;
+ private String mId;
+
+ public PhoneAccountHandle(
+ ComponentName componentName,
+ String id) {
+ mComponentName = componentName;
+ mId = id;
+ }
+
+ /**
+ * The {@code ComponentName} of the {@link android.telecom.ConnectionService} which is
+ * responsible for making phone calls using this {@code PhoneAccountHandle}.
+ *
+ * @return A suitable {@code ComponentName}.
+ */
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ /**
+ * A string that uniquely distinguishes this particular {@code PhoneAccountHandle} from all the
+ * others supported by the {@link ConnectionService} that created it.
+ * <p>
+ * A {@code ConnectionService} must select identifiers that are stable for the lifetime of
+ * their users' relationship with their service, across many Android devices. For example, a
+ * good set of identifiers might be the email addresses with which with users registered for
+ * their accounts with a particular service. Depending on how a service chooses to operate,
+ * a bad set of identifiers might be an increasing series of integers
+ * ({@code 0}, {@code 1}, {@code 2}, ...) that are generated locally on each phone and could
+ * collide with values generated on other phones or after a data wipe of a given phone.
+ *
+ * @return A service-specific unique identifier for this {@code PhoneAccountHandle}.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mComponentName) + Objects.hashCode(mId);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append(mComponentName)
+ .append(", ")
+ .append(mId)
+ .toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other != null &&
+ other instanceof PhoneAccountHandle &&
+ Objects.equals(((PhoneAccountHandle) other).getComponentName(),
+ getComponentName()) &&
+ Objects.equals(((PhoneAccountHandle) other).getId(), getId());
+ }
+
+ //
+ // Parcelable implementation.
+ //
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(mComponentName, flags);
+ out.writeString(mId);
+ }
+
+ public static final Creator<PhoneAccountHandle> CREATOR = new Creator<PhoneAccountHandle>() {
+ @Override
+ public PhoneAccountHandle createFromParcel(Parcel in) {
+ return new PhoneAccountHandle(in);
+ }
+
+ @Override
+ public PhoneAccountHandle[] newArray(int size) {
+ return new PhoneAccountHandle[size];
+ }
+ };
+
+ private PhoneAccountHandle(Parcel in) {
+ mComponentName = in.readParcelable(getClass().getClassLoader());
+ mId = in.readString();
+ }
+}
diff --git a/telecomm/java/android/telecom/PhoneCapabilities.java b/telecomm/java/android/telecom/PhoneCapabilities.java
new file mode 100644
index 0000000..e73dfe2
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneCapabilities.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 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;
+
+/**
+ * Defines capabilities a phone call can support, such as conference calling and video telephony.
+ * Also defines properties of a phone call, such as whether it is using VoLTE technology.
+ */
+public final class PhoneCapabilities {
+ /** Call can currently be put on hold or unheld. */
+ public static final int HOLD = 0x00000001;
+
+ /** Call supports the hold feature. */
+ public static final int SUPPORT_HOLD = 0x00000002;
+
+ /**
+ * Calls within a conference can be merged. Some connection services create a conference call
+ * only after two calls have been merged. However, a conference call can also be added the
+ * moment there are more than one call. CDMA calls are implemented in this way because the call
+ * actions are more limited when more than one call exists. This flag allows merge to be exposed
+ * as a capability on the conference call instead of individual calls.
+ */
+ public static final int MERGE_CONFERENCE = 0x00000004;
+
+ /** Calls withing a conference can be swapped between foreground and background. */
+ public static final int SWAP_CONFERENCE = 0x00000008;
+
+ /** Call currently supports adding another call to this one. */
+ public static final int ADD_CALL = 0x00000010;
+
+ /** Call supports responding via text option. */
+ public static final int RESPOND_VIA_TEXT = 0x00000020;
+
+ /** Call can be muted. */
+ public static final int MUTE = 0x00000040;
+
+ /**
+ * Call supports conference call management. This capability only applies to conference calls
+ * which can have other calls as children.
+ */
+ public static final int MANAGE_CONFERENCE = 0x00000080;
+
+ /**
+ * Local device supports video telephony.
+ * @hide
+ */
+ public static final int SUPPORTS_VT_LOCAL = 0x00000100;
+
+ /**
+ * Remote device supports video telephony.
+ * @hide
+ */
+ public static final int SUPPORTS_VT_REMOTE = 0x00000200;
+
+ /**
+ * Call is using voice over LTE.
+ * @hide
+ */
+ public static final int VoLTE = 0x00000400;
+
+ /**
+ * Call is using voice over WIFI.
+ * @hide
+ */
+ public static final int VoWIFI = 0x00000800;
+
+ /**
+ * Call is able to be separated from its parent {@code Conference}, if any.
+ */
+ public static final int SEPARATE_FROM_CONFERENCE = 0x00001000;
+
+ /**
+ * Call is able to be individually disconnected when in a {@code Conference}.
+ */
+ public static final int DISCONNECT_FROM_CONFERENCE = 0x00002000;
+
+ public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CONFERENCE | SWAP_CONFERENCE
+ | ADD_CALL | RESPOND_VIA_TEXT | MUTE | MANAGE_CONFERENCE | SEPARATE_FROM_CONFERENCE
+ | DISCONNECT_FROM_CONFERENCE;
+
+ public static String toString(int capabilities) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[Capabilities:");
+ if ((capabilities & HOLD) != 0) {
+ builder.append(" HOLD");
+ }
+ if ((capabilities & SUPPORT_HOLD) != 0) {
+ builder.append(" SUPPORT_HOLD");
+ }
+ if ((capabilities & MERGE_CONFERENCE) != 0) {
+ builder.append(" MERGE_CONFERENCE");
+ }
+ if ((capabilities & SWAP_CONFERENCE) != 0) {
+ builder.append(" SWAP_CONFERENCE");
+ }
+ if ((capabilities & ADD_CALL) != 0) {
+ builder.append(" ADD_CALL");
+ }
+ if ((capabilities & RESPOND_VIA_TEXT) != 0) {
+ builder.append(" RESPOND_VIA_TEXT");
+ }
+ if ((capabilities & MUTE) != 0) {
+ builder.append(" MUTE");
+ }
+ if ((capabilities & MANAGE_CONFERENCE) != 0) {
+ builder.append(" MANAGE_CONFERENCE");
+ }
+ if ((capabilities & SUPPORTS_VT_LOCAL) != 0) {
+ builder.append(" SUPPORTS_VT_LOCAL");
+ }
+ if ((capabilities & SUPPORTS_VT_REMOTE) != 0) {
+ builder.append(" SUPPORTS_VT_REMOTE");
+ }
+ if ((capabilities & VoLTE) != 0) {
+ builder.append(" VoLTE");
+ }
+ if ((capabilities & VoWIFI) != 0) {
+ builder.append(" VoWIFI");
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+ private PhoneCapabilities() {}
+}
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
new file mode 100644
index 0000000..f931bc5
--- /dev/null
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -0,0 +1,212 @@
+/*
+ * 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.IConnectionService;
+
+import android.os.RemoteException;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+/**
+ * Represents a conference call which can contain any number of {@link Connection} objects.
+ */
+public final class RemoteConference {
+
+ public abstract static class Callback {
+ public void onStateChanged(RemoteConference conference, int oldState, int newState) {}
+ public void onDisconnected(RemoteConference conference, DisconnectCause disconnectCause) {}
+ public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {}
+ public void onConnectionRemoved(RemoteConference conference, RemoteConnection connection) {}
+ public void onCapabilitiesChanged(RemoteConference conference, int capabilities) {}
+ public void onDestroyed(RemoteConference conference) {}
+ }
+
+ private final String mId;
+ private final IConnectionService mConnectionService;
+
+ private final Set<Callback> mCallbacks = new CopyOnWriteArraySet<>();
+ private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>();
+ private final List<RemoteConnection> mUnmodifiableChildConnections =
+ Collections.unmodifiableList(mChildConnections);
+
+ private int mState = Connection.STATE_NEW;
+ private DisconnectCause mDisconnectCause;
+ private int mCallCapabilities;
+
+ /** {@hide} */
+ RemoteConference(String id, IConnectionService connectionService) {
+ mId = id;
+ mConnectionService = connectionService;
+ }
+
+ /** {@hide} */
+ String getId() {
+ return mId;
+ }
+
+ /** {@hide} */
+ void setDestroyed() {
+ for (RemoteConnection connection : mChildConnections) {
+ connection.setConference(null);
+ }
+ for (Callback c : mCallbacks) {
+ c.onDestroyed(this);
+ }
+ }
+
+ /** {@hide} */
+ void setState(int newState) {
+ if (newState != Connection.STATE_ACTIVE &&
+ newState != Connection.STATE_HOLDING &&
+ newState != Connection.STATE_DISCONNECTED) {
+ Log.w(this, "Unsupported state transition for Conference call.",
+ Connection.stateToString(newState));
+ return;
+ }
+
+ if (mState != newState) {
+ int oldState = mState;
+ mState = newState;
+ for (Callback c : mCallbacks) {
+ c.onStateChanged(this, oldState, newState);
+ }
+ }
+ }
+
+ /** {@hide} */
+ void addConnection(RemoteConnection connection) {
+ if (!mChildConnections.contains(connection)) {
+ mChildConnections.add(connection);
+ connection.setConference(this);
+ for (Callback c : mCallbacks) {
+ c.onConnectionAdded(this, connection);
+ }
+ }
+ }
+
+ /** {@hide} */
+ void removeConnection(RemoteConnection connection) {
+ if (mChildConnections.contains(connection)) {
+ mChildConnections.remove(connection);
+ connection.setConference(null);
+ for (Callback c : mCallbacks) {
+ c.onConnectionRemoved(this, connection);
+ }
+ }
+ }
+
+ /** {@hide} */
+ void setCallCapabilities(int capabilities) {
+ if (mCallCapabilities != capabilities) {
+ mCallCapabilities = capabilities;
+ for (Callback c : mCallbacks) {
+ c.onCapabilitiesChanged(this, mCallCapabilities);
+ }
+ }
+ }
+
+ /** {@hide} */
+ void setDisconnected(DisconnectCause disconnectCause) {
+ if (mState != Connection.STATE_DISCONNECTED) {
+ mDisconnectCause = disconnectCause;
+ setState(Connection.STATE_DISCONNECTED);
+ for (Callback c : mCallbacks) {
+ c.onDisconnected(this, disconnectCause);
+ }
+ }
+ }
+
+ public final List<RemoteConnection> getConnections() {
+ return mUnmodifiableChildConnections;
+ }
+
+ public final int getState() {
+ return mState;
+ }
+
+ public final int getCallCapabilities() {
+ return mCallCapabilities;
+ }
+
+ public void disconnect() {
+ try {
+ mConnectionService.disconnect(mId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void separate(RemoteConnection connection) {
+ if (mChildConnections.contains(connection)) {
+ try {
+ mConnectionService.splitFromConference(connection.getId());
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void hold() {
+ try {
+ mConnectionService.hold(mId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void unhold() {
+ try {
+ mConnectionService.unhold(mId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public DisconnectCause getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ public void playDtmfTone(char digit) {
+ try {
+ mConnectionService.playDtmfTone(mId, digit);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void stopDtmfTone() {
+ try {
+ mConnectionService.stopDtmfTone(mId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void setAudioState(AudioState state) {
+ try {
+ mConnectionService.onAudioStateChanged(mId, state);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public final void registerCallback(Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ public final void unregisterCallback(Callback callback) {
+ mCallbacks.remove(callback);
+ }
+}
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
new file mode 100644
index 0000000..9a094df
--- /dev/null
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -0,0 +1,898 @@
+/*
+ * 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.IConnectionService;
+import com.android.internal.telecom.IVideoCallback;
+import com.android.internal.telecom.IVideoProvider;
+
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+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;
+
+/**
+ * A connection provided to a {@link ConnectionService} by another {@code ConnectionService}
+ * running in a different process.
+ *
+ * @see ConnectionService#createRemoteOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
+ * @see ConnectionService#createRemoteIncomingConnection(PhoneAccountHandle, ConnectionRequest)
+ */
+public final class RemoteConnection {
+
+ public static abstract class Callback {
+ /**
+ * Invoked when the state of this {@code RemoteConnection} has changed. See
+ * {@link #getState()}.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param state The new state of the {@code RemoteConnection}.
+ */
+ public void onStateChanged(RemoteConnection connection, int state) {}
+
+ /**
+ * Invoked when this {@code RemoteConnection} is disconnected.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param disconnectCause The ({@see DisconnectCause}) associated with this failed
+ * connection.
+ */
+ public void onDisconnected(
+ RemoteConnection connection,
+ DisconnectCause disconnectCause) {}
+
+ /**
+ * Invoked when this {@code RemoteConnection} is requesting ringback. See
+ * {@link #isRingbackRequested()}.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param ringback Whether the {@code RemoteConnection} is requesting ringback.
+ */
+ public void onRingbackRequested(RemoteConnection connection, boolean ringback) {}
+
+ /**
+ * Indicates that the call capabilities of this {@code RemoteConnection} have changed.
+ * See {@link #getCallCapabilities()}.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param callCapabilities The new call capabilities of the {@code RemoteConnection}.
+ */
+ public void onCallCapabilitiesChanged(RemoteConnection connection, int callCapabilities) {}
+
+ /**
+ * Invoked when the post-dial sequence in the outgoing {@code Connection} has reached a
+ * pause character. This causes the post-dial signals to stop pending user confirmation. An
+ * implementation should present this choice to the user and invoke
+ * {@link RemoteConnection#postDialContinue(boolean)} when the user makes the choice.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param remainingPostDialSequence The post-dial characters that remain to be sent.
+ */
+ public void onPostDialWait(RemoteConnection connection, String remainingPostDialSequence) {}
+
+ /**
+ * Indicates that the VOIP audio status of this {@code RemoteConnection} has changed.
+ * See {@link #isVoipAudioMode()}.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param isVoip Whether the new audio state of the {@code RemoteConnection} is VOIP.
+ */
+ public void onVoipAudioChanged(RemoteConnection connection, boolean isVoip) {}
+
+ /**
+ * Indicates that the status hints of this {@code RemoteConnection} have changed. See
+ * {@link #getStatusHints()} ()}.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param statusHints The new status hints of the {@code RemoteConnection}.
+ */
+ public void onStatusHintsChanged(RemoteConnection connection, StatusHints statusHints) {}
+
+ /**
+ * Indicates that the address (e.g., phone number) of this {@code RemoteConnection} has
+ * changed. See {@link #getAddress()} and {@link #getAddressPresentation()}.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param address The new address of the {@code RemoteConnection}.
+ * @param presentation The presentation requirements for the address.
+ * See {@link TelecomManager} for valid values.
+ */
+ public void onAddressChanged(RemoteConnection connection, Uri address, int presentation) {}
+
+ /**
+ * Indicates that the caller display name of this {@code RemoteConnection} has changed.
+ * See {@link #getCallerDisplayName()} and {@link #getCallerDisplayNamePresentation()}.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param callerDisplayName The new caller display name of the {@code RemoteConnection}.
+ * @param presentation The presentation requirements for the handle.
+ * See {@link TelecomManager} for valid values.
+ */
+ public void onCallerDisplayNameChanged(
+ RemoteConnection connection, String callerDisplayName, int presentation) {}
+
+ /**
+ * Indicates that the video state of this {@code RemoteConnection} has changed.
+ * See {@link #getVideoState()}.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param videoState The new video state of the {@code RemoteConnection}.
+ * @hide
+ */
+ public void onVideoStateChanged(RemoteConnection connection, int videoState) {}
+
+ /**
+ * Indicates that this {@code RemoteConnection} has been destroyed. No further requests
+ * should be made to the {@code RemoteConnection}, and references to it should be cleared.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ */
+ public void onDestroyed(RemoteConnection connection) {}
+
+ /**
+ * Indicates that the {@code RemoteConnection}s with which this {@code RemoteConnection}
+ * may be asked to create a conference has changed.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param conferenceableConnections The {@code RemoteConnection}s with which this
+ * {@code RemoteConnection} may be asked to create a conference.
+ */
+ public void onConferenceableConnectionsChanged(
+ RemoteConnection connection,
+ List<RemoteConnection> conferenceableConnections) {}
+
+ /**
+ * Indicates that the {@code VideoProvider} associated with this {@code RemoteConnection}
+ * has changed.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param videoProvider The new {@code VideoProvider} associated with this
+ * {@code RemoteConnection}.
+ * @hide
+ */
+ public void onVideoProviderChanged(
+ RemoteConnection connection, VideoProvider videoProvider) {}
+
+ /**
+ * Indicates that the {@code RemoteConference} that this {@code RemoteConnection} is a part
+ * of has changed.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param conference The {@code RemoteConference} of which this {@code RemoteConnection} is
+ * a part, which may be {@code null}.
+ */
+ public void onConferenceChanged(
+ RemoteConnection connection,
+ RemoteConference conference) {}
+ }
+
+ /** {@hide} */
+ public static class VideoProvider {
+
+ public abstract static class Listener {
+ public void onReceiveSessionModifyRequest(
+ VideoProvider videoProvider,
+ VideoProfile videoProfile) {}
+
+ public void onReceiveSessionModifyResponse(
+ VideoProvider videoProvider,
+ int status,
+ VideoProfile requestedProfile,
+ VideoProfile responseProfile) {}
+
+ public void onHandleCallSessionEvent(VideoProvider videoProvider, int event) {}
+
+ public void onPeerDimensionsChanged(VideoProvider videoProvider, int width, int height) {}
+
+ public void onCallDataUsageChanged(VideoProvider videoProvider, int dataUsage) {}
+
+ public void onCameraCapabilitiesChanged(
+ VideoProvider videoProvider,
+ CameraCapabilities cameraCapabilities) {}
+ }
+
+ private final IVideoCallback mVideoCallbackDelegate = new IVideoCallback() {
+ @Override
+ public void receiveSessionModifyRequest(VideoProfile videoProfile) {
+ for (Listener l : mListeners) {
+ l.onReceiveSessionModifyRequest(VideoProvider.this, videoProfile);
+ }
+ }
+
+ @Override
+ public void receiveSessionModifyResponse(int status, VideoProfile requestedProfile,
+ VideoProfile responseProfile) {
+ for (Listener l : mListeners) {
+ l.onReceiveSessionModifyResponse(
+ VideoProvider.this,
+ status,
+ requestedProfile,
+ responseProfile);
+ }
+ }
+
+ @Override
+ public void handleCallSessionEvent(int event) {
+ for (Listener l : mListeners) {
+ l.onHandleCallSessionEvent(VideoProvider.this, event);
+ }
+ }
+
+ @Override
+ public void changePeerDimensions(int width, int height) {
+ for (Listener l : mListeners) {
+ l.onPeerDimensionsChanged(VideoProvider.this, width, height);
+ }
+ }
+
+ @Override
+ public void changeCallDataUsage(int dataUsage) {
+ for (Listener l : mListeners) {
+ l.onCallDataUsageChanged(VideoProvider.this, dataUsage);
+ }
+ }
+
+ @Override
+ public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
+ for (Listener l : mListeners) {
+ l.onCameraCapabilitiesChanged(VideoProvider.this, cameraCapabilities);
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+
+ private final VideoCallbackServant mVideoCallbackServant =
+ new VideoCallbackServant(mVideoCallbackDelegate);
+
+ private final IVideoProvider mVideoProviderBinder;
+
+ /**
+ * 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));
+
+ public VideoProvider(IVideoProvider videoProviderBinder) {
+ mVideoProviderBinder = videoProviderBinder;
+ try {
+ mVideoProviderBinder.setVideoCallback(mVideoCallbackServant.getStub().asBinder());
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void addListener(Listener l) {
+ mListeners.add(l);
+ }
+
+ public void removeListener(Listener l) {
+ mListeners.remove(l);
+ }
+
+ public void setCamera(String cameraId) {
+ try {
+ mVideoProviderBinder.setCamera(cameraId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void setPreviewSurface(Surface surface) {
+ try {
+ mVideoProviderBinder.setPreviewSurface(surface);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void setDisplaySurface(Surface surface) {
+ try {
+ mVideoProviderBinder.setDisplaySurface(surface);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void setDeviceOrientation(int rotation) {
+ try {
+ mVideoProviderBinder.setDeviceOrientation(rotation);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void setZoom(float value) {
+ try {
+ mVideoProviderBinder.setZoom(value);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void sendSessionModifyRequest(VideoProfile reqProfile) {
+ try {
+ mVideoProviderBinder.sendSessionModifyRequest(reqProfile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void sendSessionModifyResponse(VideoProfile responseProfile) {
+ try {
+ mVideoProviderBinder.sendSessionModifyResponse(responseProfile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void requestCameraCapabilities() {
+ try {
+ mVideoProviderBinder.requestCameraCapabilities();
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void requestCallDataUsage() {
+ try {
+ mVideoProviderBinder.requestCallDataUsage();
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void setPauseImage(String uri) {
+ try {
+ mVideoProviderBinder.setPauseImage(uri);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ private IConnectionService mConnectionService;
+ private final String mConnectionId;
+ /**
+ * 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<Callback> mCallbacks = Collections.newSetFromMap(
+ new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
+ private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
+ private final List<RemoteConnection> mUnmodifiableconferenceableConnections =
+ Collections.unmodifiableList(mConferenceableConnections);
+
+ private int mState = Connection.STATE_NEW;
+ private DisconnectCause mDisconnectCause;
+ private boolean mRingbackRequested;
+ private boolean mConnected;
+ private int mCallCapabilities;
+ private int mVideoState;
+ private VideoProvider mVideoProvider;
+ private boolean mIsVoipAudioMode;
+ private StatusHints mStatusHints;
+ private Uri mAddress;
+ private int mAddressPresentation;
+ private String mCallerDisplayName;
+ private int mCallerDisplayNamePresentation;
+ private RemoteConference mConference;
+
+ /**
+ * @hide
+ */
+ RemoteConnection(
+ String id,
+ IConnectionService connectionService,
+ ConnectionRequest request) {
+ mConnectionId = id;
+ mConnectionService = connectionService;
+ mConnected = true;
+ mState = Connection.STATE_INITIALIZING;
+ }
+
+ /**
+ * Create a RemoteConnection which is used for failed connections. Note that using it for any
+ * "real" purpose will almost certainly fail. Callers should note the failure and act
+ * accordingly (moving on to another RemoteConnection, for example)
+ *
+ * @param disconnectCause The reason for the failed connection.
+ * @hide
+ */
+ RemoteConnection(DisconnectCause disconnectCause) {
+ this("NULL", null, null);
+ mConnected = false;
+ mState = Connection.STATE_DISCONNECTED;
+ mDisconnectCause = disconnectCause;
+ }
+
+ /**
+ * Adds a callback to this {@code RemoteConnection}.
+ *
+ * @param callback A {@code Callback}.
+ */
+ public void registerCallback(Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ /**
+ * Removes a callback from this {@code RemoteConnection}.
+ *
+ * @param callback A {@code Callback}.
+ */
+ public void unregisterCallback(Callback callback) {
+ if (callback != null) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ /**
+ * Obtains the state of this {@code RemoteConnection}.
+ *
+ * @return A state value, chosen from the {@code STATE_*} constants.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * @return For a {@link Connection#STATE_DISCONNECTED} {@code RemoteConnection}, the
+ * disconnect cause expressed as a code chosen from among those declared in
+ * {@link DisconnectCause}.
+ */
+ public DisconnectCause getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ /**
+ * @return A bitmask of the capabilities of the {@code RemoteConnection}, as defined in
+ * {@link PhoneCapabilities}.
+ */
+ public int getCallCapabilities() {
+ return mCallCapabilities;
+ }
+
+ /**
+ * @return {@code true} if the {@code RemoteConnection}'s current audio mode is VOIP.
+ */
+ public boolean isVoipAudioMode() {
+ return mIsVoipAudioMode;
+ }
+
+ /**
+ * @return The current {@link StatusHints} of this {@code RemoteConnection},
+ * or {@code null} if none have been set.
+ */
+ public StatusHints getStatusHints() {
+ return mStatusHints;
+ }
+
+ /**
+ * @return The address (e.g., phone number) to which the {@code RemoteConnection} is currently
+ * connected.
+ */
+ public Uri getAddress() {
+ return mAddress;
+ }
+
+ /**
+ * @return The presentation requirements for the address. See {@link TelecomManager} for valid
+ * values.
+ */
+ public int getAddressPresentation() {
+ return mAddressPresentation;
+ }
+
+ /**
+ * @return The display name for the caller.
+ */
+ public CharSequence getCallerDisplayName() {
+ return mCallerDisplayName;
+ }
+
+ /**
+ * @return The presentation requirements for the caller display name. See
+ * {@link TelecomManager} for valid values.
+ */
+ public int getCallerDisplayNamePresentation() {
+ return mCallerDisplayNamePresentation;
+ }
+
+ /**
+ * @return The video state of the {@code RemoteConnection}. See
+ * {@link VideoProfile.VideoState}.
+ * @hide
+ */
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ /**
+ * @return The video provider associated with this {@code RemoteConnection}.
+ * @hide
+ */
+ public final VideoProvider getVideoProvider() {
+ return mVideoProvider;
+ }
+
+ /**
+ * @return Whether the {@code RemoteConnection} is requesting that the framework play a
+ * ringback tone on its behalf.
+ */
+ public boolean isRingbackRequested() {
+ return false;
+ }
+
+ /**
+ * Instructs this {@code RemoteConnection} to abort.
+ */
+ public void abort() {
+ try {
+ if (mConnected) {
+ mConnectionService.abort(mConnectionId);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
+ */
+ public void answer() {
+ try {
+ if (mConnected) {
+ mConnectionService.answer(mConnectionId);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
+ * @param videoState The video state in which to answer the call.
+ * @hide
+ */
+ public void answer(int videoState) {
+ try {
+ if (mConnected) {
+ mConnectionService.answerVideo(mConnectionId, videoState);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to reject.
+ */
+ public void reject() {
+ try {
+ if (mConnected) {
+ mConnectionService.reject(mConnectionId);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs this {@code RemoteConnection} to go on hold.
+ */
+ public void hold() {
+ try {
+ if (mConnected) {
+ mConnectionService.hold(mConnectionId);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs this {@link Connection#STATE_HOLDING} call to release from hold.
+ */
+ public void unhold() {
+ try {
+ if (mConnected) {
+ mConnectionService.unhold(mConnectionId);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs this {@code RemoteConnection} to disconnect.
+ */
+ public void disconnect() {
+ try {
+ if (mConnected) {
+ mConnectionService.disconnect(mConnectionId);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs this {@code RemoteConnection} to play a dual-tone multi-frequency signaling
+ * (DTMF) tone.
+ *
+ * Any other currently playing DTMF tone in the specified call is immediately stopped.
+ *
+ * @param digit A character representing the DTMF digit for which to play the tone. This
+ * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
+ */
+ public void playDtmfTone(char digit) {
+ try {
+ if (mConnected) {
+ mConnectionService.playDtmfTone(mConnectionId, digit);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs this {@code RemoteConnection} to stop any dual-tone multi-frequency signaling
+ * (DTMF) tone currently playing.
+ *
+ * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
+ * currently playing, this method will do nothing.
+ */
+ public void stopDtmfTone() {
+ try {
+ if (mConnected) {
+ mConnectionService.stopDtmfTone(mConnectionId);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs this {@code RemoteConnection} to continue playing a post-dial DTMF string.
+ *
+ * A post-dial DTMF string is a string of digits following the first instance of either
+ * {@link TelecomManager#DTMF_CHARACTER_WAIT} or {@link TelecomManager#DTMF_CHARACTER_PAUSE}.
+ * These digits are immediately sent as DTMF tones to the recipient as soon as the
+ * connection is made.
+ *
+ * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
+ * {@code RemoteConnection} will temporarily pause playing the tones for a pre-defined period
+ * of time.
+ *
+ * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
+ * {@code RemoteConnection} will pause playing the tones and notify callbackss via
+ * {@link Callback#onPostDialWait(RemoteConnection, String)}. At this point, the in-call app
+ * should display to the user an indication of this state and an affordance to continue
+ * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
+ * app should invoke the {@link #postDialContinue(boolean)} method.
+ *
+ * @param proceed Whether or not to continue with the post-dial sequence.
+ */
+ public void postDialContinue(boolean proceed) {
+ try {
+ if (mConnected) {
+ mConnectionService.onPostDialContinue(mConnectionId, proceed);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Set the audio state of this {@code RemoteConnection}.
+ *
+ * @param state The audio state of this {@code RemoteConnection}.
+ */
+ public void setAudioState(AudioState state) {
+ try {
+ if (mConnected) {
+ mConnectionService.onAudioStateChanged(mConnectionId, state);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Obtain the {@code RemoteConnection}s with which this {@code RemoteConnection} may be
+ * successfully asked to create a conference with.
+ *
+ * @return The {@code RemoteConnection}s with which this {@code RemoteConnection} may be
+ * merged into a {@link RemoteConference}.
+ */
+ public List<RemoteConnection> getConferenceableConnections() {
+ return mUnmodifiableconferenceableConnections;
+ }
+
+ /**
+ * Obtain the {@code RemoteConference} that this {@code RemoteConnection} may be a part
+ * of, or {@code null} if there is no such {@code RemoteConference}.
+ *
+ * @return A {@code RemoteConference} or {@code null};
+ */
+ public RemoteConference getConference() {
+ return mConference;
+ }
+
+ /** {@hide} */
+ String getId() {
+ return mConnectionId;
+ }
+
+ /** {@hide} */
+ IConnectionService getConnectionService() {
+ return mConnectionService;
+ }
+
+ /**
+ * @hide
+ */
+ void setState(int state) {
+ if (mState != state) {
+ mState = state;
+ for (Callback c: mCallbacks) {
+ c.onStateChanged(this, state);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ void setDisconnected(DisconnectCause disconnectCause) {
+ if (mState != Connection.STATE_DISCONNECTED) {
+ mState = Connection.STATE_DISCONNECTED;
+ mDisconnectCause = disconnectCause;
+
+ for (Callback c : mCallbacks) {
+ c.onDisconnected(this, mDisconnectCause);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ void setRingbackRequested(boolean ringback) {
+ if (mRingbackRequested != ringback) {
+ mRingbackRequested = ringback;
+ for (Callback c : mCallbacks) {
+ c.onRingbackRequested(this, ringback);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ void setCallCapabilities(int callCapabilities) {
+ mCallCapabilities = callCapabilities;
+ for (Callback c : mCallbacks) {
+ c.onCallCapabilitiesChanged(this, callCapabilities);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ void setDestroyed() {
+ if (!mCallbacks.isEmpty()) {
+ // Make sure that the callbacks are notified that the call is destroyed first.
+ if (mState != Connection.STATE_DISCONNECTED) {
+ setDisconnected(
+ new DisconnectCause(DisconnectCause.ERROR, "Connection destroyed."));
+ }
+
+ for (Callback c : mCallbacks) {
+ c.onDestroyed(this);
+ }
+ mCallbacks.clear();
+
+ mConnected = false;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ void setPostDialWait(String remainingDigits) {
+ for (Callback c : mCallbacks) {
+ c.onPostDialWait(this, remainingDigits);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ void setVideoState(int videoState) {
+ mVideoState = videoState;
+ for (Callback c : mCallbacks) {
+ c.onVideoStateChanged(this, videoState);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ void setVideoProvider(VideoProvider videoProvider) {
+ mVideoProvider = videoProvider;
+ for (Callback c : mCallbacks) {
+ c.onVideoProviderChanged(this, videoProvider);
+ }
+ }
+
+ /** @hide */
+ void setIsVoipAudioMode(boolean isVoip) {
+ mIsVoipAudioMode = isVoip;
+ for (Callback c : mCallbacks) {
+ c.onVoipAudioChanged(this, isVoip);
+ }
+ }
+
+ /** @hide */
+ void setStatusHints(StatusHints statusHints) {
+ mStatusHints = statusHints;
+ for (Callback c : mCallbacks) {
+ c.onStatusHintsChanged(this, statusHints);
+ }
+ }
+
+ /** @hide */
+ void setAddress(Uri address, int presentation) {
+ mAddress = address;
+ mAddressPresentation = presentation;
+ for (Callback c : mCallbacks) {
+ c.onAddressChanged(this, address, presentation);
+ }
+ }
+
+ /** @hide */
+ void setCallerDisplayName(String callerDisplayName, int presentation) {
+ mCallerDisplayName = callerDisplayName;
+ mCallerDisplayNamePresentation = presentation;
+ for (Callback c : mCallbacks) {
+ c.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
+ }
+ }
+
+ /** @hide */
+ void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
+ mConferenceableConnections.clear();
+ mConferenceableConnections.addAll(conferenceableConnections);
+ for (Callback c : mCallbacks) {
+ c.onConferenceableConnectionsChanged(this, mUnmodifiableconferenceableConnections);
+ }
+ }
+
+ /** @hide */
+ void setConference(RemoteConference conference) {
+ if (mConference != conference) {
+ mConference = conference;
+ for (Callback c : mCallbacks) {
+ c.onConferenceChanged(this, conference);
+ }
+ }
+ }
+
+ /**
+ * Create a RemoteConnection represents a failure, and which will be in
+ * {@link Connection#STATE_DISCONNECTED}. Attempting to use it for anything will almost
+ * certainly result in bad things happening. Do not do this.
+ *
+ * @return a failed {@link RemoteConnection}
+ *
+ * @hide
+ */
+ public static RemoteConnection failure(DisconnectCause disconnectCause) {
+ return new RemoteConnection(disconnectCause);
+ }
+}
diff --git a/telecomm/java/android/telecom/RemoteConnectionManager.java b/telecomm/java/android/telecom/RemoteConnectionManager.java
new file mode 100644
index 0000000..0366509
--- /dev/null
+++ b/telecomm/java/android/telecom/RemoteConnectionManager.java
@@ -0,0 +1,88 @@
+/*
+ * 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
+ R* limitations under the License.
+ */
+
+package android.telecom;
+
+import android.content.ComponentName;
+import android.os.RemoteException;
+
+import com.android.internal.telecom.IConnectionService;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @hide
+ */
+public class RemoteConnectionManager {
+ private final Map<ComponentName, RemoteConnectionService> mRemoteConnectionServices =
+ new HashMap<>();
+ private final ConnectionService mOurConnectionServiceImpl;
+
+ public RemoteConnectionManager(ConnectionService ourConnectionServiceImpl) {
+ mOurConnectionServiceImpl = ourConnectionServiceImpl;
+ }
+
+ void addConnectionService(
+ ComponentName componentName,
+ IConnectionService outgoingConnectionServiceRpc) {
+ if (!mRemoteConnectionServices.containsKey(componentName)) {
+ try {
+ RemoteConnectionService remoteConnectionService = new RemoteConnectionService(
+ outgoingConnectionServiceRpc,
+ mOurConnectionServiceImpl);
+ mRemoteConnectionServices.put(componentName, remoteConnectionService);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ public RemoteConnection createRemoteConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request,
+ boolean isIncoming) {
+ PhoneAccountHandle accountHandle = request.getAccountHandle();
+ if (accountHandle == null) {
+ throw new IllegalArgumentException("accountHandle must be specified.");
+ }
+
+ ComponentName componentName = request.getAccountHandle().getComponentName();
+ if (!mRemoteConnectionServices.containsKey(componentName)) {
+ throw new UnsupportedOperationException("accountHandle not supported: "
+ + componentName);
+ }
+
+ RemoteConnectionService remoteService = mRemoteConnectionServices.get(componentName);
+ if (remoteService != null) {
+ return remoteService.createRemoteConnection(
+ connectionManagerPhoneAccount, request, isIncoming);
+ }
+ return null;
+ }
+
+ public void conferenceRemoteConnections(RemoteConnection a, RemoteConnection b) {
+ if (a.getConnectionService() == b.getConnectionService()) {
+ try {
+ a.getConnectionService().conference(a.getId(), b.getId());
+ } catch (RemoteException e) {
+ }
+ } else {
+ Log.w(this, "Request to conference incompatible remote connections (%s,%s) (%s,%s)",
+ a.getConnectionService(), a.getId(),
+ b.getConnectionService(), b.getId());
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
new file mode 100644
index 0000000..03b38c2
--- /dev/null
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -0,0 +1,383 @@
+/*
+ * 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 android.net.Uri;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+
+import com.android.internal.telecom.IConnectionService;
+import com.android.internal.telecom.IConnectionServiceAdapter;
+import com.android.internal.telecom.IVideoProvider;
+import com.android.internal.telecom.RemoteServiceCallback;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Remote connection service which other connection services can use to place calls on their behalf.
+ *
+ * @hide
+ */
+final class RemoteConnectionService {
+
+ private static final RemoteConnection NULL_CONNECTION =
+ new RemoteConnection("NULL", null, null);
+
+ private static final RemoteConference NULL_CONFERENCE =
+ new RemoteConference("NULL", null);
+
+ private final IConnectionServiceAdapter mServantDelegate = new IConnectionServiceAdapter() {
+ @Override
+ public void handleCreateConnectionComplete(
+ String id,
+ ConnectionRequest request,
+ ParcelableConnection parcel) {
+ RemoteConnection connection =
+ findConnectionForAction(id, "handleCreateConnectionSuccessful");
+ if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) {
+ mPendingConnections.remove(connection);
+ // Unconditionally initialize the connection ...
+ connection.setCallCapabilities(parcel.getCapabilities());
+ connection.setAddress(
+ parcel.getHandle(), parcel.getHandlePresentation());
+ connection.setCallerDisplayName(
+ parcel.getCallerDisplayName(),
+ parcel.getCallerDisplayNamePresentation());
+ // Set state after handle so that the client can identify the connection.
+ connection.setState(parcel.getState());
+ List<RemoteConnection> conferenceable = new ArrayList<>();
+ for (String confId : parcel.getConferenceableConnectionIds()) {
+ if (mConnectionById.containsKey(confId)) {
+ conferenceable.add(mConnectionById.get(confId));
+ }
+ }
+ connection.setConferenceableConnections(conferenceable);
+ connection.setVideoState(parcel.getVideoState());
+ if (connection.getState() == Connection.STATE_DISCONNECTED) {
+ // ... then, if it was created in a disconnected state, that indicates
+ // failure on the providing end, so immediately mark it destroyed
+ connection.setDestroyed();
+ }
+ }
+ }
+
+ @Override
+ public void setActive(String callId) {
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "setActive")
+ .setState(Connection.STATE_ACTIVE);
+ } else {
+ findConferenceForAction(callId, "setActive")
+ .setState(Connection.STATE_ACTIVE);
+ }
+ }
+
+ @Override
+ public void setRinging(String callId) {
+ findConnectionForAction(callId, "setRinging")
+ .setState(Connection.STATE_RINGING);
+ }
+
+ @Override
+ public void setDialing(String callId) {
+ findConnectionForAction(callId, "setDialing")
+ .setState(Connection.STATE_DIALING);
+ }
+
+ @Override
+ public void setDisconnected(String callId, DisconnectCause disconnectCause) {
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "setDisconnected")
+ .setDisconnected(disconnectCause);
+ } else {
+ findConferenceForAction(callId, "setDisconnected")
+ .setDisconnected(disconnectCause);
+ }
+ }
+
+ @Override
+ public void setOnHold(String callId) {
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "setOnHold")
+ .setState(Connection.STATE_HOLDING);
+ } else {
+ findConferenceForAction(callId, "setOnHold")
+ .setState(Connection.STATE_HOLDING);
+ }
+ }
+
+ @Override
+ public void setRingbackRequested(String callId, boolean ringing) {
+ findConnectionForAction(callId, "setRingbackRequested")
+ .setRingbackRequested(ringing);
+ }
+
+ @Override
+ public void setCallCapabilities(String callId, int callCapabilities) {
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "setCallCapabilities")
+ .setCallCapabilities(callCapabilities);
+ } else {
+ findConferenceForAction(callId, "setCallCapabilities")
+ .setCallCapabilities(callCapabilities);
+ }
+ }
+
+ @Override
+ public void setIsConferenced(String callId, String conferenceCallId) {
+ // Note: callId should not be null; conferenceCallId may be null
+ RemoteConnection connection =
+ findConnectionForAction(callId, "setIsConferenced");
+ if (connection != NULL_CONNECTION) {
+ if (conferenceCallId == null) {
+ // 'connection' is being split from its conference
+ if (connection.getConference() != null) {
+ connection.getConference().removeConnection(connection);
+ }
+ } else {
+ RemoteConference conference =
+ findConferenceForAction(conferenceCallId, "setIsConferenced");
+ if (conference != NULL_CONFERENCE) {
+ conference.addConnection(connection);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void addConferenceCall(
+ final String callId,
+ ParcelableConference parcel) {
+ RemoteConference conference = new RemoteConference(callId,
+ mOutgoingConnectionServiceRpc);
+
+ for (String id : parcel.getConnectionIds()) {
+ RemoteConnection c = mConnectionById.get(id);
+ if (c != null) {
+ conference.addConnection(c);
+ }
+ }
+
+ if (conference.getConnections().size() == 0) {
+ // A conference was created, but none of its connections are ones that have been
+ // created by, and therefore being tracked by, this remote connection service. It
+ // is of no interest to us.
+ return;
+ }
+
+ conference.setState(parcel.getState());
+ conference.setCallCapabilities(parcel.getCapabilities());
+ mConferenceById.put(callId, conference);
+ conference.registerCallback(new RemoteConference.Callback() {
+ @Override
+ public void onDestroyed(RemoteConference c) {
+ mConferenceById.remove(callId);
+ maybeDisconnectAdapter();
+ }
+ });
+
+ mOurConnectionServiceImpl.addRemoteConference(conference);
+ }
+
+ @Override
+ public void removeCall(String callId) {
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "removeCall")
+ .setDestroyed();
+ } else {
+ findConferenceForAction(callId, "removeCall")
+ .setDestroyed();
+ }
+ }
+
+ @Override
+ public void onPostDialWait(String callId, String remaining) {
+ findConnectionForAction(callId, "onPostDialWait")
+ .setPostDialWait(remaining);
+ }
+
+ @Override
+ public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+ // Not supported from remote connection service.
+ }
+
+ @Override
+ public void setVideoProvider(String callId, IVideoProvider videoProvider) {
+ findConnectionForAction(callId, "setVideoProvider")
+ .setVideoProvider(new RemoteConnection.VideoProvider(videoProvider));
+ }
+
+ @Override
+ public void setVideoState(String callId, int videoState) {
+ findConnectionForAction(callId, "setVideoState")
+ .setVideoState(videoState);
+ }
+
+ @Override
+ public void setIsVoipAudioMode(String callId, boolean isVoip) {
+ findConnectionForAction(callId, "setIsVoipAudioMode")
+ .setIsVoipAudioMode(isVoip);
+ }
+
+ @Override
+ public void setStatusHints(String callId, StatusHints statusHints) {
+ findConnectionForAction(callId, "setStatusHints")
+ .setStatusHints(statusHints);
+ }
+
+ @Override
+ public void setAddress(String callId, Uri address, int presentation) {
+ findConnectionForAction(callId, "setAddress")
+ .setAddress(address, presentation);
+ }
+
+ @Override
+ public void setCallerDisplayName(String callId, String callerDisplayName,
+ int presentation) {
+ findConnectionForAction(callId, "setCallerDisplayName")
+ .setCallerDisplayName(callerDisplayName, presentation);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final void setConferenceableConnections(
+ String callId, List<String> conferenceableConnectionIds) {
+ List<RemoteConnection> conferenceable = new ArrayList<>();
+ for (String id : conferenceableConnectionIds) {
+ if (mConnectionById.containsKey(id)) {
+ conferenceable.add(mConnectionById.get(id));
+ }
+ }
+
+ findConnectionForAction(callId, "setConferenceableConnections")
+ .setConferenceableConnections(conferenceable);
+ }
+ };
+
+ private final ConnectionServiceAdapterServant mServant =
+ new ConnectionServiceAdapterServant(mServantDelegate);
+
+ private final DeathRecipient mDeathRecipient = new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ for (RemoteConnection c : mConnectionById.values()) {
+ c.setDestroyed();
+ }
+ for (RemoteConference c : mConferenceById.values()) {
+ c.setDestroyed();
+ }
+ mConnectionById.clear();
+ mConferenceById.clear();
+ mPendingConnections.clear();
+ mOutgoingConnectionServiceRpc.asBinder().unlinkToDeath(mDeathRecipient, 0);
+ }
+ };
+
+ private final IConnectionService mOutgoingConnectionServiceRpc;
+ private final ConnectionService mOurConnectionServiceImpl;
+ private final Map<String, RemoteConnection> mConnectionById = new HashMap<>();
+ private final Map<String, RemoteConference> mConferenceById = new HashMap<>();
+ private final Set<RemoteConnection> mPendingConnections = new HashSet<>();
+
+ RemoteConnectionService(
+ IConnectionService outgoingConnectionServiceRpc,
+ ConnectionService ourConnectionServiceImpl) throws RemoteException {
+ mOutgoingConnectionServiceRpc = outgoingConnectionServiceRpc;
+ mOutgoingConnectionServiceRpc.asBinder().linkToDeath(mDeathRecipient, 0);
+ mOurConnectionServiceImpl = ourConnectionServiceImpl;
+ }
+
+ @Override
+ public String toString() {
+ return "[RemoteCS - " + mOutgoingConnectionServiceRpc.asBinder().toString() + "]";
+ }
+
+ final RemoteConnection createRemoteConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request,
+ boolean isIncoming) {
+ final String id = UUID.randomUUID().toString();
+ final ConnectionRequest newRequest = new ConnectionRequest(
+ request.getAccountHandle(),
+ request.getAddress(),
+ request.getExtras(),
+ request.getVideoState());
+ try {
+ if (mConnectionById.isEmpty()) {
+ mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub());
+ }
+ RemoteConnection connection =
+ new RemoteConnection(id, mOutgoingConnectionServiceRpc, newRequest);
+ mPendingConnections.add(connection);
+ mConnectionById.put(id, connection);
+ mOutgoingConnectionServiceRpc.createConnection(
+ connectionManagerPhoneAccount,
+ id,
+ newRequest,
+ isIncoming);
+ connection.registerCallback(new RemoteConnection.Callback() {
+ @Override
+ public void onDestroyed(RemoteConnection connection) {
+ mConnectionById.remove(id);
+ maybeDisconnectAdapter();
+ }
+ });
+ return connection;
+ } catch (RemoteException e) {
+ return RemoteConnection.failure(
+ new DisconnectCause(DisconnectCause.ERROR, e.toString()));
+ }
+ }
+
+ private RemoteConnection findConnectionForAction(
+ String callId, String action) {
+ if (mConnectionById.containsKey(callId)) {
+ return mConnectionById.get(callId);
+ }
+ Log.w(this, "%s - Cannot find Connection %s", action, callId);
+ return NULL_CONNECTION;
+ }
+
+ private RemoteConference findConferenceForAction(
+ String callId, String action) {
+ if (mConferenceById.containsKey(callId)) {
+ return mConferenceById.get(callId);
+ }
+ Log.w(this, "%s - Cannot find Conference %s", action, callId);
+ return NULL_CONFERENCE;
+ }
+
+ private void maybeDisconnectAdapter() {
+ if (mConnectionById.isEmpty() && mConferenceById.isEmpty()) {
+ try {
+ mOutgoingConnectionServiceRpc.removeConnectionServiceAdapter(mServant.getStub());
+ } catch (RemoteException e) {
+ }
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Response.java b/telecomm/java/android/telecom/Response.java
new file mode 100644
index 0000000..ce7a761
--- /dev/null
+++ b/telecomm/java/android/telecom/Response.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+/**
+ * @hide
+ */
+public interface Response<IN, OUT> {
+
+ /**
+ * Provide a set of results.
+ *
+ * @param request The original request.
+ * @param result The results.
+ */
+ void onResult(IN request, OUT... result);
+
+ /**
+ * Indicates the inability to provide results.
+ *
+ * @param request The original request.
+ * @param code An integer code indicating the reason for failure.
+ * @param msg A message explaining the reason for failure.
+ */
+ void onError(IN request, int code, String msg);
+}
diff --git a/telecomm/java/android/telecom/StatusHints.aidl b/telecomm/java/android/telecom/StatusHints.aidl
new file mode 100644
index 0000000..ae7df2e
--- /dev/null
+++ b/telecomm/java/android/telecom/StatusHints.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2008 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;
+
+/**
+ * {@hide}
+ */
+parcelable StatusHints;
diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java
new file mode 100644
index 0000000..a32eae7
--- /dev/null
+++ b/telecomm/java/android/telecom/StatusHints.java
@@ -0,0 +1,150 @@
+/*
+ * 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 android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.MissingResourceException;
+import java.util.Objects;
+
+/**
+ * Contains status label and icon displayed in the in-call UI.
+ */
+public final class StatusHints implements Parcelable {
+
+ private final ComponentName mPackageName;
+ private final CharSequence mLabel;
+ private final int mIconResId;
+ private final Bundle mExtras;
+
+ public StatusHints(ComponentName packageName, CharSequence label, int iconResId,
+ Bundle extras) {
+ mPackageName = packageName;
+ mLabel = label;
+ mIconResId = iconResId;
+ mExtras = extras;
+ }
+
+ /**
+ * @return A package used to load the icon.
+ */
+ public ComponentName getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * @return The label displayed in the in-call UI.
+ */
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * The icon resource ID for the icon to show.
+ *
+ * @return A resource ID.
+ */
+ public int getIconResId() {
+ return mIconResId;
+ }
+
+ /**
+ * @return An icon displayed in the in-call UI.
+ */
+ public Drawable getIcon(Context context) {
+ return getIcon(context, mIconResId);
+ }
+
+ /**
+ * @return Extra data used to display status.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(mPackageName, flags);
+ out.writeCharSequence(mLabel);
+ out.writeInt(mIconResId);
+ out.writeParcelable(mExtras, 0);
+ }
+
+ public static final Creator<StatusHints> CREATOR
+ = new Creator<StatusHints>() {
+ public StatusHints createFromParcel(Parcel in) {
+ return new StatusHints(in);
+ }
+
+ public StatusHints[] newArray(int size) {
+ return new StatusHints[size];
+ }
+ };
+
+ private StatusHints(Parcel in) {
+ mPackageName = in.readParcelable(getClass().getClassLoader());
+ mLabel = in.readCharSequence();
+ mIconResId = in.readInt();
+ mExtras = in.readParcelable(getClass().getClassLoader());
+ }
+
+ private Drawable getIcon(Context context, int resId) {
+ Context packageContext;
+ try {
+ packageContext = context.createPackageContext(mPackageName.getPackageName(), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(this, e, "Cannot find package %s", mPackageName.getPackageName());
+ return null;
+ }
+ try {
+ return packageContext.getDrawable(resId);
+ } catch (MissingResourceException e) {
+ Log.e(this, e, "Cannot find icon %d in package %s",
+ resId, mPackageName.getPackageName());
+ return null;
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other != null && other instanceof StatusHints) {
+ StatusHints otherHints = (StatusHints) other;
+ return Objects.equals(otherHints.getPackageName(), getPackageName()) &&
+ Objects.equals(otherHints.getLabel(), getLabel()) &&
+ otherHints.getIconResId() == getIconResId() &&
+ Objects.equals(otherHints.getExtras(), getExtras());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mPackageName) + Objects.hashCode(mLabel) + mIconResId +
+ Objects.hashCode(mExtras);
+ }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
new file mode 100644
index 0000000..a91d92f
--- /dev/null
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -0,0 +1,858 @@
+/*
+ * 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 android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telecom.ITelecomService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Provides access to Telecom-related functionality.
+ * TODO: Move this all into PhoneManager.
+ */
+public class TelecomManager {
+
+ /**
+ * Activity action: Starts the UI for handing an incoming call. This intent starts the in-call
+ * UI by notifying the Telecom system that an incoming call exists for a specific call service
+ * (see {@link android.telecom.ConnectionService}). Telecom reads the Intent extras to find
+ * and bind to the appropriate {@link android.telecom.ConnectionService} which Telecom will
+ * ultimately use to control and get information about the call.
+ * <p>
+ * Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT_HANDLE} contains the component name of the
+ * {@link android.telecom.ConnectionService} that Telecom should bind to. Telecom will then
+ * ask the connection service for more information about the call prior to showing any UI.
+ *
+ * @hide
+ */
+ public static final String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
+
+ /**
+ * The {@link android.content.Intent} action used to configure a
+ * {@link android.telecom.ConnectionService}.
+ */
+ public static final String ACTION_CONNECTION_SERVICE_CONFIGURE =
+ "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
+
+ /**
+ * The {@link android.content.Intent} action used to show the call settings page.
+ */
+ public static final String ACTION_SHOW_CALL_SETTINGS =
+ "android.telecom.action.SHOW_CALL_SETTINGS";
+
+ /**
+ * The {@link android.content.Intent} action used to show the settings page used to configure
+ * {@link PhoneAccount} preferences.
+ */
+ public static final String ACTION_CHANGE_PHONE_ACCOUNTS =
+ "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
+
+ /**
+ * Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
+ * determines whether the speakerphone should be automatically turned on for an outgoing call.
+ */
+ public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE =
+ "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
+
+ /**
+ * Optional extra for {@link android.content.Intent#ACTION_CALL} containing an integer that
+ * determines the desired video state for an outgoing call.
+ * Valid options:
+ * {@link VideoProfile.VideoState#AUDIO_ONLY},
+ * {@link VideoProfile.VideoState#BIDIRECTIONAL},
+ * {@link VideoProfile.VideoState#RX_ENABLED},
+ * {@link VideoProfile.VideoState#TX_ENABLED}.
+ * @hide
+ */
+ public static final String EXTRA_START_CALL_WITH_VIDEO_STATE =
+ "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
+
+ /**
+ * The extra used with an {@link android.content.Intent#ACTION_CALL} and
+ * {@link android.content.Intent#ACTION_DIAL} {@code Intent} to specify a
+ * {@link PhoneAccountHandle} to use when making the call.
+ * <p class="note">
+ * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
+ "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
+
+ /**
+ * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains
+ * metadata about the call. This {@link Bundle} will be returned to the
+ * {@link ConnectionService}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_INCOMING_CALL_EXTRAS =
+ "android.telecom.extra.INCOMING_CALL_EXTRAS";
+
+ /**
+ * Optional extra for {@link android.content.Intent#ACTION_CALL} and
+ * {@link android.content.Intent#ACTION_DIAL} {@code Intent} containing a {@link Bundle}
+ * which contains metadata about the call. This {@link Bundle} will be saved into
+ * {@code Call.Details}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_OUTGOING_CALL_EXTRAS =
+ "android.telecom.extra.OUTGOING_CALL_EXTRAS";
+
+ /**
+ * Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
+ * containing the disconnect code.
+ */
+ public static final String EXTRA_CALL_DISCONNECT_CAUSE =
+ "android.telecom.extra.CALL_DISCONNECT_CAUSE";
+
+ /**
+ * Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
+ * containing the disconnect message.
+ */
+ public static final String EXTRA_CALL_DISCONNECT_MESSAGE =
+ "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
+
+ /**
+ * Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
+ * containing the component name of the associated connection service.
+ */
+ public static final String EXTRA_CONNECTION_SERVICE =
+ "android.telecom.extra.CONNECTION_SERVICE";
+
+ /**
+ * An optional {@link android.content.Intent#ACTION_CALL} intent extra denoting the
+ * package name of the app specifying an alternative gateway for the call.
+ * The value is a string.
+ *
+ * (The following comment corresponds to the all GATEWAY_* extras)
+ * An app which sends the {@link android.content.Intent#ACTION_CALL} intent can specify an
+ * alternative address to dial which is different from the one specified and displayed to
+ * the user. This alternative address is referred to as the gateway address.
+ */
+ public static final String GATEWAY_PROVIDER_PACKAGE =
+ "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
+
+ /**
+ * An optional {@link android.content.Intent#ACTION_CALL} intent extra corresponding to the
+ * original address to dial for the call. This is used when an alternative gateway address is
+ * provided to recall the original address.
+ * The value is a {@link android.net.Uri}.
+ *
+ * (See {@link #GATEWAY_PROVIDER_PACKAGE} for details)
+ */
+ public static final String GATEWAY_ORIGINAL_ADDRESS =
+ "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
+
+ /**
+ * The number which the party on the other side of the line will see (and use to return the
+ * call).
+ * <p>
+ * {@link ConnectionService}s which interact with {@link RemoteConnection}s should only populate
+ * this if the {@link android.telephony.TelephonyManager#getLine1Number()} value, as that is the
+ * user's expected caller ID.
+ */
+ public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
+
+ /**
+ * The dual tone multi-frequency signaling character sent to indicate the dialing system should
+ * pause for a predefined period.
+ */
+ public static final char DTMF_CHARACTER_PAUSE = ',';
+
+ /**
+ * The dual-tone multi-frequency signaling character sent to indicate the dialing system should
+ * wait for user confirmation before proceeding.
+ */
+ public static final char DTMF_CHARACTER_WAIT = ';';
+
+ /**
+ * TTY (teletypewriter) mode is off.
+ *
+ * @hide
+ */
+ public static final int TTY_MODE_OFF = 0;
+
+ /**
+ * TTY (teletypewriter) mode is on. The speaker is off and the microphone is muted. The user
+ * will communicate with the remote party by sending and receiving text messages.
+ *
+ * @hide
+ */
+ public static final int TTY_MODE_FULL = 1;
+
+ /**
+ * TTY (teletypewriter) mode is in hearing carryover mode (HCO). The microphone is muted but the
+ * speaker is on. The user will communicate with the remote party by sending text messages and
+ * hearing an audible reply.
+ *
+ * @hide
+ */
+ public static final int TTY_MODE_HCO = 2;
+
+ /**
+ * TTY (teletypewriter) mode is in voice carryover mode (VCO). The speaker is off but the
+ * microphone is still on. User will communicate with the remote party by speaking and receiving
+ * text message replies.
+ *
+ * @hide
+ */
+ public static final int TTY_MODE_VCO = 3;
+
+ /**
+ * Broadcast intent action indicating that the current TTY mode has changed. An intent extra
+ * provides this state as an int.
+ *
+ * @see #EXTRA_CURRENT_TTY_MODE
+ * @hide
+ */
+ public static final String ACTION_CURRENT_TTY_MODE_CHANGED =
+ "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
+
+ /**
+ * The lookup key for an int that indicates the current TTY mode.
+ * Valid modes are:
+ * - {@link #TTY_MODE_OFF}
+ * - {@link #TTY_MODE_FULL}
+ * - {@link #TTY_MODE_HCO}
+ * - {@link #TTY_MODE_VCO}
+ *
+ * @hide
+ */
+ public static final String EXTRA_CURRENT_TTY_MODE =
+ "android.telecom.intent.extra.CURRENT_TTY_MODE";
+
+ /**
+ * Broadcast intent action indicating that the TTY preferred operating mode has changed. An
+ * intent extra provides the new mode as an int.
+ *
+ * @see #EXTRA_TTY_PREFERRED_MODE
+ * @hide
+ */
+ public static final String ACTION_TTY_PREFERRED_MODE_CHANGED =
+ "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
+
+ /**
+ * The lookup key for an int that indicates preferred TTY mode. Valid modes are: -
+ * {@link #TTY_MODE_OFF} - {@link #TTY_MODE_FULL} - {@link #TTY_MODE_HCO} -
+ * {@link #TTY_MODE_VCO}
+ *
+ * @hide
+ */
+ public static final String EXTRA_TTY_PREFERRED_MODE =
+ "android.telecom.intent.extra.TTY_PREFERRED";
+
+ /**
+ * The following 4 constants define how properties such as phone numbers and names are
+ * displayed to the user.
+ */
+
+ /** Property is displayed normally. */
+ public static final int PRESENTATION_ALLOWED = 1;
+
+ /** Property was blocked. */
+ public static final int PRESENTATION_RESTRICTED = 2;
+
+ /** Presentation was not specified or is unknown. */
+ public static final int PRESENTATION_UNKNOWN = 3;
+
+ /** Property should be displayed as a pay phone. */
+ public static final int PRESENTATION_PAYPHONE = 4;
+
+ private static final String TAG = "TelecomManager";
+
+ private final Context mContext;
+
+ /**
+ * @hide
+ */
+ public static TelecomManager from(Context context) {
+ return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ }
+
+ /**
+ * @hide
+ */
+ public TelecomManager(Context context) {
+ Context appContext = context.getApplicationContext();
+ if (appContext != null) {
+ mContext = appContext;
+ } else {
+ mContext = context;
+ }
+ }
+
+ /**
+ * Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
+ * calls with a specified URI scheme. This {@code PhoneAccount} will always be a member of the
+ * list which is returned from calling {@link #getCallCapablePhoneAccounts()}.
+ * <p>
+ * Apps must be prepared for this method to return {@code null}, indicating that there currently
+ * exists no user-chosen default {@code PhoneAccount}. In this case, apps wishing to initiate a
+ * phone call must either create their {@link android.content.Intent#ACTION_CALL} or
+ * {@link android.content.Intent#ACTION_DIAL} {@code Intent} with no
+ * {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE}, or present the user with an affordance to
+ * select one of the elements of {@link #getCallCapablePhoneAccounts()}.
+ * <p>
+ * An {@link android.content.Intent#ACTION_CALL} or {@link android.content.Intent#ACTION_DIAL}
+ * {@code Intent} with no {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE} is valid, and
+ * subsequent steps in the phone call flow are responsible for presenting the user with an
+ * affordance, if necessary, to choose a {@code PhoneAccount}.
+ *
+ * @param uriScheme The URI scheme.
+ */
+ public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getDefaultOutgoingPhoneAccount(uriScheme);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getDefaultOutgoingPhoneAccount", e);
+ }
+ return null;
+ }
+
+ /**
+ * Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
+ * calls. This {@code PhoneAccount} will always be a member of the list which is returned from
+ * calling {@link #getCallCapablePhoneAccounts()}
+ *
+ * Apps must be prepared for this method to return {@code null}, indicating that there currently
+ * exists no user-chosen default {@code PhoneAccount}.
+ *
+ * @return The user outgoing phone account selected by the user.
+ * @hide
+ */
+ public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getUserSelectedOutgoingPhoneAccount();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getUserSelectedOutgoingPhoneAccount", e);
+ }
+ return null;
+ }
+
+ /**
+ * Sets the default account for making outgoing phone calls.
+ * @hide
+ */
+ public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().setUserSelectedOutgoingPhoneAccount(accountHandle);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#setUserSelectedOutgoingPhoneAccount");
+ }
+ }
+
+ /**
+ * Return a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
+ * calls.
+ *
+ * @see #EXTRA_PHONE_ACCOUNT_HANDLE
+ * @return A list of {@code PhoneAccountHandle} objects.
+ */
+ public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getCallCapablePhoneAccounts();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts", e);
+ }
+ return new ArrayList<>();
+ }
+
+ /**
+ * Returns the current SIM call manager. Apps must be prepared for this method to return
+ * {@code null}, indicating that there currently exists no user-chosen default
+ * {@code PhoneAccount}.
+ * @return The phone account handle of the current sim call manager.
+ * @hide
+ */
+ public PhoneAccountHandle getSimCallManager() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getSimCallManager();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getSimCallManager");
+ }
+ return null;
+ }
+
+ /**
+ * Sets the SIM call manager to the specified phone account.
+ * @param accountHandle The phone account handle of the account to set as the sim call manager.
+ * @hide
+ */
+ public void setSimCallManager(PhoneAccountHandle accountHandle) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().setSimCallManager(accountHandle);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#setSimCallManager");
+ }
+ }
+
+ /**
+ * Returns the list of registered SIM call managers.
+ * @return List of registered SIM call managers.
+ * @hide
+ */
+ public List<PhoneAccountHandle> getSimCallManagers() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getSimCallManagers();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getSimCallManagers");
+ }
+ return new ArrayList<>();
+ }
+
+ /**
+ * Returns the current connection manager. Apps must be prepared for this method to return
+ * {@code null}, indicating that there currently exists no user-chosen default
+ * {@code PhoneAccount}.
+ *
+ * @return The phone account handle of the current connection manager.
+ */
+ public PhoneAccountHandle getConnectionManager() {
+ return getSimCallManager();
+ }
+
+ /**
+ * Returns a list of the {@link PhoneAccountHandle}s which can be used to make and receive phone
+ * calls which support the specified URI scheme.
+ * <P>
+ * For example, invoking with {@code "tel"} will find all {@link PhoneAccountHandle}s which
+ * support telephone calls (e.g. URIs such as {@code tel:555-555-1212}). Invoking with
+ * {@code "sip"} will find all {@link PhoneAccountHandle}s which support SIP calls (e.g. URIs
+ * such as {@code sip:example@sipexample.com}).
+ *
+ * @param uriScheme The URI scheme.
+ * @return A list of {@code PhoneAccountHandle} objects supporting the URI scheme.
+ */
+ public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getPhoneAccountsSupportingScheme(uriScheme);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getPhoneAccountsSupportingScheme", e);
+ }
+ return new ArrayList<>();
+ }
+
+ /**
+ * Determine whether the device has more than one account registered that can make and receive
+ * phone calls.
+ *
+ * @return {@code true} if the device has more than one account registered and {@code false}
+ * otherwise.
+ */
+ public boolean hasMultipleCallCapableAccounts() {
+ return getCallCapablePhoneAccounts().size() > 1;
+ }
+
+ /**
+ * Return the {@link PhoneAccount} for a specified {@link PhoneAccountHandle}. Object includes
+ * resources which can be used in a user interface.
+ *
+ * @param account The {@link PhoneAccountHandle}.
+ * @return The {@link PhoneAccount} object.
+ */
+ public PhoneAccount getPhoneAccount(PhoneAccountHandle account) {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getPhoneAccount(account);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getPhoneAccount", e);
+ }
+ return null;
+ }
+
+ /**
+ * Returns a count of all {@link PhoneAccount}s.
+ *
+ * @return The count of {@link PhoneAccount}s.
+ * @hide
+ */
+ @SystemApi
+ public int getAllPhoneAccountsCount() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getAllPhoneAccountsCount();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccountsCount", e);
+ }
+ return 0;
+ }
+
+ /**
+ * Returns a list of all {@link PhoneAccount}s.
+ *
+ * @return All {@link PhoneAccount}s.
+ * @hide
+ */
+ @SystemApi
+ public List<PhoneAccount> getAllPhoneAccounts() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getAllPhoneAccounts();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccounts", e);
+ }
+ return Collections.EMPTY_LIST;
+ }
+
+ /**
+ * Returns a list of all {@link PhoneAccountHandle}s.
+ *
+ * @return All {@link PhoneAccountHandle}s.
+ * @hide
+ */
+ @SystemApi
+ public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getAllPhoneAccountHandles();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccountHandles", e);
+ }
+ return Collections.EMPTY_LIST;
+ }
+
+ /**
+ * Register a {@link PhoneAccount} for use by the system.
+ *
+ * @param account The complete {@link PhoneAccount}.
+ */
+ public void registerPhoneAccount(PhoneAccount account) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().registerPhoneAccount(account);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#registerPhoneAccount", e);
+ }
+ }
+
+ /**
+ * Remove a {@link PhoneAccount} registration from the system.
+ *
+ * @param accountHandle A {@link PhoneAccountHandle} for the {@link PhoneAccount} to unregister.
+ */
+ public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().unregisterPhoneAccount(accountHandle);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#unregisterPhoneAccount", e);
+ }
+ }
+
+ /**
+ * Remove all Accounts that belong to the calling package from the system.
+ */
+ @SystemApi
+ public void clearAccounts() {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().clearAccounts(mContext.getPackageName());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#clearAccounts", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public ComponentName getDefaultPhoneApp() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getDefaultPhoneApp();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the default phone app.", e);
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether there is an ongoing phone call (can be in dialing, ringing, active or holding
+ * states).
+ * <p>
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ * </p>
+ */
+ @SystemApi
+ public boolean isInCall() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().isInCall();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling isInCall().", e);
+ }
+ return false;
+ }
+
+ /**
+ * Returns one of the following constants that represents the current state of Telecom:
+ *
+ * {@link TelephonyManager#CALL_STATE_RINGING}
+ * {@link TelephonyManager#CALL_STATE_OFFHOOK}
+ * {@link TelephonyManager#CALL_STATE_IDLE}
+ * @hide
+ */
+ @SystemApi
+ public int getCallState() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getCallState();
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "RemoteException calling getCallState().", e);
+ }
+ return TelephonyManager.CALL_STATE_IDLE;
+ }
+
+ /**
+ * Returns whether there currently exists is a ringing incoming-call.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isRinging() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().isRinging();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get ringing state of phone app.", e);
+ }
+ return false;
+ }
+
+ /**
+ * Ends an ongoing call.
+ * TODO: L-release - need to convert all invocations of ITelecomService#endCall to use this
+ * method (clockwork & gearhead).
+ * @hide
+ */
+ @SystemApi
+ public boolean endCall() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().endCall();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#endCall", e);
+ }
+ return false;
+ }
+
+ /**
+ * If there is a ringing incoming call, this method accepts the call on behalf of the user.
+ * TODO: L-release - need to convert all invocation of ITelecmmService#answerRingingCall to use
+ * this method (clockwork & gearhead).
+ *
+ * @hide
+ */
+ @SystemApi
+ public void acceptRingingCall() {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().acceptRingingCall();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#acceptRingingCall", e);
+ }
+ }
+
+ /**
+ * Silences the ringer if a ringing call exists.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void silenceRinger() {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().silenceRinger();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#silenceRinger", e);
+ }
+ }
+
+ /**
+ * Returns whether TTY is supported on this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isTtySupported() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().isTtySupported();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get TTY supported state.", e);
+ }
+ return false;
+ }
+
+ /**
+ * Returns the current TTY mode of the device. For TTY to be on the user must enable it in
+ * settings and have a wired headset plugged in.
+ * Valid modes are:
+ * - {@link TelecomManager#TTY_MODE_OFF}
+ * - {@link TelecomManager#TTY_MODE_FULL}
+ * - {@link TelecomManager#TTY_MODE_HCO}
+ * - {@link TelecomManager#TTY_MODE_VCO}
+ * @hide
+ */
+ public int getCurrentTtyMode() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getCurrentTtyMode();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the current TTY mode.", e);
+ }
+ return TTY_MODE_OFF;
+ }
+
+ /**
+ * Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
+ * has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
+ * with {@link #registerPhoneAccount}. Once invoked, this method will cause the system to bind
+ * to the {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
+ * additional information about the call (See
+ * {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI.
+ *
+ * @param phoneAccount A {@link PhoneAccountHandle} registered with
+ * {@link #registerPhoneAccount}.
+ * @param extras A bundle that will be passed through to
+ * {@link ConnectionService#onCreateIncomingConnection}.
+ */
+ public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().addNewIncomingCall(
+ phoneAccount, extras == null ? new Bundle() : extras);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);
+ }
+ }
+
+ /**
+ * Processes the specified dial string as an MMI code.
+ * MMI codes are any sequence of characters entered into the dialpad that contain a "*" or "#".
+ * Some of these sequences launch special behavior through handled by Telephony.
+ * <p>
+ * Requires that the method-caller be set as the system dialer app.
+ * </p>
+ *
+ * @param dialString The digits to dial.
+ * @return True if the digits were processed as an MMI code, false otherwise.
+ */
+ public boolean handleMmi(String dialString) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.handlePinMmi(dialString);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#handlePinMmi", e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes the missed-call notification if one is present.
+ * <p>
+ * Requires that the method-caller be set as the system dialer app.
+ * </p>
+ */
+ public void cancelMissedCallsNotification() {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.cancelMissedCallsNotification();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#cancelMissedCallsNotification", e);
+ }
+ }
+ }
+
+ /**
+ * Brings the in-call screen to the foreground if there is an ongoing call. If there is
+ * currently no ongoing call, then this method does nothing.
+ * <p>
+ * Requires that the method-caller be set as the system dialer app or have the
+ * {@link android.Manifest.permission#READ_PHONE_STATE} permission.
+ * </p>
+ *
+ * @param showDialpad Brings up the in-call dialpad as part of showing the in-call screen.
+ */
+ public void showInCallScreen(boolean showDialpad) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.showInCallScreen(showDialpad);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#showCallScreen", e);
+ }
+ }
+ }
+
+ private ITelecomService getTelecomService() {
+ return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
+ }
+
+ private boolean isServiceConnected() {
+ boolean isConnected = getTelecomService() != null;
+ if (!isConnected) {
+ Log.w(TAG, "Telecom Service not found.");
+ }
+ return isConnected;
+ }
+}
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
new file mode 100644
index 0000000..925058e
--- /dev/null
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -0,0 +1,247 @@
+/*
+ * 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 android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.InCallService.VideoCall;
+import android.view.Surface;
+
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.IVideoCallback;
+import com.android.internal.telecom.IVideoProvider;
+
+/**
+ * Implementation of a Video Call, which allows InCallUi to communicate commands to the underlying
+ * {@link Connection.VideoProvider}, and direct callbacks from the
+ * {@link Connection.VideoProvider} to the appropriate {@link VideoCall.Listener}.
+ *
+ * {@hide}
+ */
+public class VideoCallImpl extends VideoCall {
+ private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 1;
+ private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
+ private static final int MSG_HANDLE_CALL_SESSION_EVENT = 3;
+ private static final int MSG_CHANGE_PEER_DIMENSIONS = 4;
+ private static final int MSG_CHANGE_CALL_DATA_USAGE = 5;
+ private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 6;
+
+ private final IVideoProvider mVideoProvider;
+ private final VideoCallListenerBinder mBinder;
+ private VideoCall.Listener mVideoCallListener;
+
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mVideoProvider.asBinder().unlinkToDeath(this, 0);
+ }
+ };
+
+ /**
+ * IVideoCallback stub implementation.
+ */
+ private final class VideoCallListenerBinder extends IVideoCallback.Stub {
+ @Override
+ public void receiveSessionModifyRequest(VideoProfile videoProfile) {
+ mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_REQUEST,
+ videoProfile).sendToTarget();
+ }
+
+ @Override
+ public void receiveSessionModifyResponse(int status, VideoProfile requestProfile,
+ VideoProfile responseProfile) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = status;
+ args.arg2 = requestProfile;
+ args.arg3 = responseProfile;
+ mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args).sendToTarget();
+ }
+
+ @Override
+ public void handleCallSessionEvent(int event) {
+ mHandler.obtainMessage(MSG_HANDLE_CALL_SESSION_EVENT, event).sendToTarget();
+ }
+
+ @Override
+ public void changePeerDimensions(int width, int height) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = width;
+ args.arg2 = height;
+ mHandler.obtainMessage(MSG_CHANGE_PEER_DIMENSIONS, args).sendToTarget();
+ }
+
+ @Override
+ public void changeCallDataUsage(int dataUsage) {
+ mHandler.obtainMessage(MSG_CHANGE_CALL_DATA_USAGE, dataUsage).sendToTarget();
+ }
+
+ @Override
+ public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
+ mHandler.obtainMessage(MSG_CHANGE_CAMERA_CAPABILITIES,
+ cameraCapabilities).sendToTarget();
+ }
+ }
+
+ /** Default handler used to consolidate binder method calls onto a single thread. */
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (mVideoCallListener == null) {
+ return;
+ }
+
+ SomeArgs args;
+ switch (msg.what) {
+ case MSG_RECEIVE_SESSION_MODIFY_REQUEST:
+ mVideoCallListener.onSessionModifyRequestReceived((VideoProfile) msg.obj);
+ break;
+ case MSG_RECEIVE_SESSION_MODIFY_RESPONSE:
+ args = (SomeArgs) msg.obj;
+ try {
+ int status = (int) args.arg1;
+ VideoProfile requestProfile = (VideoProfile) args.arg2;
+ VideoProfile responseProfile = (VideoProfile) args.arg3;
+
+ mVideoCallListener.onSessionModifyResponseReceived(
+ status, requestProfile, responseProfile);
+ } finally {
+ args.recycle();
+ }
+ break;
+ case MSG_HANDLE_CALL_SESSION_EVENT:
+ mVideoCallListener.onCallSessionEvent((int) msg.obj);
+ break;
+ case MSG_CHANGE_PEER_DIMENSIONS:
+ args = (SomeArgs) msg.obj;
+ try {
+ int width = (int) args.arg1;
+ int height = (int) args.arg2;
+ mVideoCallListener.onPeerDimensionsChanged(width, height);
+ } finally {
+ args.recycle();
+ }
+ break;
+ case MSG_CHANGE_CALL_DATA_USAGE:
+ mVideoCallListener.onCallDataUsageChanged(msg.arg1);
+ break;
+ case MSG_CHANGE_CAMERA_CAPABILITIES:
+ mVideoCallListener.onCameraCapabilitiesChanged(
+ (CameraCapabilities) msg.obj);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ /** {@hide} */
+ VideoCallImpl(IVideoProvider videoProvider) throws RemoteException {
+ mVideoProvider = videoProvider;
+ mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
+
+ mBinder = new VideoCallListenerBinder();
+ mVideoProvider.setVideoCallback(mBinder);
+ }
+
+ /** {@inheritDoc} */
+ public void setVideoCallListener(VideoCall.Listener videoCallListener) {
+ mVideoCallListener = videoCallListener;
+ }
+
+ /** {@inheritDoc} */
+ public void setCamera(String cameraId) {
+ try {
+ mVideoProvider.setCamera(cameraId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setPreviewSurface(Surface surface) {
+ try {
+ mVideoProvider.setPreviewSurface(surface);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setDisplaySurface(Surface surface) {
+ try {
+ mVideoProvider.setDisplaySurface(surface);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setDeviceOrientation(int rotation) {
+ try {
+ mVideoProvider.setDeviceOrientation(rotation);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setZoom(float value) {
+ try {
+ mVideoProvider.setZoom(value);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void sendSessionModifyRequest(VideoProfile requestProfile) {
+ try {
+ mVideoProvider.sendSessionModifyRequest(requestProfile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void sendSessionModifyResponse(VideoProfile responseProfile) {
+ try {
+ mVideoProvider.sendSessionModifyResponse(responseProfile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void requestCameraCapabilities() {
+ try {
+ mVideoProvider.requestCameraCapabilities();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void requestCallDataUsage() {
+ try {
+ mVideoProvider.requestCallDataUsage();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setPauseImage(String uri) {
+ try {
+ mVideoProvider.setPauseImage(uri);
+ } catch (RemoteException e) {
+ }
+ }
+} \ No newline at end of file
diff --git a/telecomm/java/android/telecom/VideoCallbackServant.java b/telecomm/java/android/telecom/VideoCallbackServant.java
new file mode 100644
index 0000000..d0e3f22
--- /dev/null
+++ b/telecomm/java/android/telecom/VideoCallbackServant.java
@@ -0,0 +1,160 @@
+/*
+ * 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
+ R* limitations under the License.
+ */
+
+package android.telecom;
+
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.IVideoCallback;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+
+/**
+ * A component that provides an RPC servant implementation of {@link IVideoCallback},
+ * posting incoming messages on the main thread on a client-supplied delegate object.
+ *
+ * TODO: Generate this and similar classes using a compiler starting from AIDL interfaces.
+ *
+ * @hide
+ */
+final class VideoCallbackServant {
+ private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 0;
+ private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 1;
+ private static final int MSG_HANDLE_CALL_SESSION_EVENT = 2;
+ private static final int MSG_CHANGE_PEER_DIMENSIONS = 3;
+ private static final int MSG_CHANGE_CALL_DATA_USAGE = 4;
+ private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 5;
+
+ private final IVideoCallback mDelegate;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ try {
+ internalHandleMessage(msg);
+ } catch (RemoteException e) {
+ }
+ }
+
+ // Internal method defined to centralize handling of RemoteException
+ private void internalHandleMessage(Message msg) throws RemoteException {
+ switch (msg.what) {
+ case MSG_RECEIVE_SESSION_MODIFY_REQUEST: {
+ mDelegate.receiveSessionModifyRequest((VideoProfile) msg.obj);
+ break;
+ }
+ case MSG_RECEIVE_SESSION_MODIFY_RESPONSE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.receiveSessionModifyResponse(
+ args.argi1,
+ (VideoProfile) args.arg1,
+ (VideoProfile) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_HANDLE_CALL_SESSION_EVENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.handleCallSessionEvent(args.argi1);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_CHANGE_PEER_DIMENSIONS: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.changePeerDimensions(args.argi1, args.argi2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_CHANGE_CALL_DATA_USAGE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.changeCallDataUsage(args.argi1);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_CHANGE_CAMERA_CAPABILITIES: {
+ mDelegate.changeCameraCapabilities((CameraCapabilities) msg.obj);
+ break;
+ }
+ }
+ }
+ };
+
+ private final IVideoCallback mStub = new IVideoCallback.Stub() {
+ @Override
+ public void receiveSessionModifyRequest(VideoProfile videoProfile) throws RemoteException {
+ mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_REQUEST, videoProfile).sendToTarget();
+ }
+
+ @Override
+ public void receiveSessionModifyResponse(int status, VideoProfile requestedProfile,
+ VideoProfile responseProfile) throws RemoteException {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = status;
+ args.arg1 = requestedProfile;
+ args.arg2 = responseProfile;
+ mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args).sendToTarget();
+ }
+
+ @Override
+ public void handleCallSessionEvent(int event) throws RemoteException {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = event;
+ mHandler.obtainMessage(MSG_HANDLE_CALL_SESSION_EVENT, args).sendToTarget();
+ }
+
+ @Override
+ public void changePeerDimensions(int width, int height) throws RemoteException {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = width;
+ args.argi2 = height;
+ mHandler.obtainMessage(MSG_CHANGE_PEER_DIMENSIONS, args).sendToTarget();
+ }
+
+ @Override
+ public void changeCallDataUsage(int dataUsage) throws RemoteException {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = dataUsage;
+ mHandler.obtainMessage(MSG_CHANGE_CALL_DATA_USAGE, args).sendToTarget();
+ }
+
+ @Override
+ public void changeCameraCapabilities(CameraCapabilities cameraCapabilities)
+ throws RemoteException {
+ mHandler.obtainMessage(MSG_CHANGE_CAMERA_CAPABILITIES, cameraCapabilities)
+ .sendToTarget();
+ }
+ };
+
+ public VideoCallbackServant(IVideoCallback delegate) {
+ mDelegate = delegate;
+ }
+
+ public IVideoCallback getStub() {
+ return mStub;
+ }
+}
diff --git a/telecomm/java/android/telecom/VideoProfile.aidl b/telecomm/java/android/telecom/VideoProfile.aidl
new file mode 100644
index 0000000..091b569
--- /dev/null
+++ b/telecomm/java/android/telecom/VideoProfile.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+parcelable VideoProfile;
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
new file mode 100644
index 0000000..f5cb054
--- /dev/null
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -0,0 +1,231 @@
+/*
+ * 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 android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents attributes of video calls.
+ *
+ * {@hide}
+ */
+public class VideoProfile implements Parcelable {
+ /**
+ * "High" video quality.
+ */
+ public static final int QUALITY_HIGH = 1;
+
+ /**
+ * "Medium" video quality.
+ */
+ public static final int QUALITY_MEDIUM = 2;
+
+ /**
+ * "Low" video quality.
+ */
+ public static final int QUALITY_LOW = 3;
+
+ /**
+ * Use default video quality.
+ */
+ public static final int QUALITY_DEFAULT = 4;
+
+ private final int mVideoState;
+
+ private final int mQuality;
+
+ /**
+ * Creates an instance of the VideoProfile
+ *
+ * @param videoState The video state.
+ */
+ public VideoProfile(int videoState) {
+ this(videoState, QUALITY_DEFAULT);
+ }
+
+ /**
+ * Creates an instance of the VideoProfile
+ *
+ * @param videoState The video state.
+ * @param quality The video quality.
+ */
+ public VideoProfile(int videoState, int quality) {
+ mVideoState = videoState;
+ mQuality = quality;
+ }
+
+ /**
+ * 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},
+ * {@link VideoProfile.VideoState#PAUSED}.
+ */
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ /**
+ * The desired video quality for the call.
+ * Valid values: {@link VideoProfile#QUALITY_HIGH}, {@link VideoProfile#QUALITY_MEDIUM},
+ * {@link VideoProfile#QUALITY_LOW}, {@link VideoProfile#QUALITY_DEFAULT}.
+ */
+ public int getQuality() {
+ return mQuality;
+ }
+
+ /**
+ * Responsible for creating VideoProfile objects from deserialized Parcels.
+ **/
+ public static final Parcelable.Creator<VideoProfile> CREATOR =
+ new Parcelable.Creator<VideoProfile> () {
+ /**
+ * Creates a MediaProfile instances from a parcel.
+ *
+ * @param source The parcel.
+ * @return The MediaProfile.
+ */
+ @Override
+ public VideoProfile createFromParcel(Parcel source) {
+ int state = source.readInt();
+ int quality = source.readInt();
+
+ ClassLoader classLoader = VideoProfile.class.getClassLoader();
+ return new VideoProfile(state, quality);
+ }
+
+ @Override
+ public VideoProfile[] newArray(int size) {
+ return new VideoProfile[size];
+ }
+ };
+
+ /**
+ * Describe the kinds of special objects contained in this Parcelable's
+ * marshalled representation.
+ *
+ * @return a bitmask indicating the set of special object types marshalled
+ * by the Parcelable.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mVideoState);
+ dest.writeInt(mQuality);
+ }
+
+ /**
+ * The video state of the call, stored as a bit-field describing whether video transmission and
+ * receipt it enabled, as well as whether the video is currently muted.
+ */
+ public static class VideoState {
+ /**
+ * Call is currently in an audio-only mode with no video transmission or receipt.
+ */
+ public static final int AUDIO_ONLY = 0x0;
+
+ /**
+ * Video transmission is enabled.
+ */
+ public static final int TX_ENABLED = 0x1;
+
+ /**
+ * Video reception is enabled.
+ */
+ public static final int RX_ENABLED = 0x2;
+
+ /**
+ * Video signal is bi-directional.
+ */
+ public static final int BIDIRECTIONAL = TX_ENABLED | RX_ENABLED;
+
+ /**
+ * Video is paused.
+ */
+ public static final int PAUSED = 0x4;
+
+ /**
+ * Whether the video state is audio only.
+ * @param videoState The video state.
+ * @return Returns true if the video state is audio only.
+ */
+ public static boolean isAudioOnly(int videoState) {
+ return !hasState(videoState, TX_ENABLED) && !hasState(videoState, RX_ENABLED);
+ }
+
+ /**
+ * Whether the video transmission is enabled.
+ * @param videoState The video state.
+ * @return Returns true if the video transmission is enabled.
+ */
+ public static boolean isTransmissionEnabled(int videoState) {
+ return hasState(videoState, TX_ENABLED);
+ }
+
+ /**
+ * Whether the video reception is enabled.
+ * @param videoState The video state.
+ * @return Returns true if the video transmission is enabled.
+ */
+ public static boolean isReceptionEnabled(int videoState) {
+ return hasState(videoState, RX_ENABLED);
+ }
+
+ /**
+ * Whether the video signal is bi-directional.
+ * @param videoState
+ * @return Returns true if the video signal is bi-directional.
+ */
+ public static boolean isBidirectional(int videoState) {
+ return hasState(videoState, BIDIRECTIONAL);
+ }
+
+ /**
+ * Whether the video is paused.
+ * @param videoState The video state.
+ * @return Returns true if the video is paused.
+ */
+ public static boolean isPaused(int videoState) {
+ return hasState(videoState, PAUSED);
+ }
+
+ /**
+ * Determines if a specified state is set in a videoState bit-mask.
+ *
+ * @param videoState The video state bit-mask.
+ * @param state The state to check.
+ * @return {@code True} if the state is set.
+ * {@hide}
+ */
+ private static boolean hasState(int videoState, int state) {
+ return (videoState & state) == state;
+ }
+ }
+}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
new file mode 100644
index 0000000..1059da3
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -0,0 +1,73 @@
+/*
+ * 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 com.android.internal.telecom;
+
+import android.os.Bundle;
+import android.telecom.AudioState;
+import android.telecom.ConnectionRequest;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.internal.telecom.IConnectionServiceAdapter;
+
+/**
+ * Internal remote interface for connection services.
+ *
+ * @see android.telecom.ConnectionService
+ *
+ * @hide
+ */
+oneway interface IConnectionService {
+ void addConnectionServiceAdapter(in IConnectionServiceAdapter adapter);
+
+ void removeConnectionServiceAdapter(in IConnectionServiceAdapter adapter);
+
+ void createConnection(
+ in PhoneAccountHandle connectionManagerPhoneAccount,
+ String callId,
+ in ConnectionRequest request,
+ boolean isIncoming);
+
+ void abort(String callId);
+
+ void answerVideo(String callId, int videoState);
+
+ void answer(String callId);
+
+ void reject(String callId);
+
+ void disconnect(String callId);
+
+ void hold(String callId);
+
+ void unhold(String callId);
+
+ void onAudioStateChanged(String activeCallId, in AudioState audioState);
+
+ void playDtmfTone(String callId, char digit);
+
+ void stopDtmfTone(String callId);
+
+ void conference(String conferenceCallId, String callId);
+
+ void splitFromConference(String callId);
+
+ void mergeConference(String conferenceCallId);
+
+ void swapConference(String conferenceCallId);
+
+ void onPostDialContinue(String callId, boolean proceed);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
new file mode 100644
index 0000000..5daa568
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -0,0 +1,80 @@
+/*
+ * 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 com.android.internal.telecom;
+
+import android.app.PendingIntent;
+import android.net.Uri;
+import android.telecom.ConnectionRequest;
+import android.telecom.DisconnectCause;
+import android.telecom.ParcelableConnection;
+import android.telecom.ParcelableConference;
+import android.telecom.StatusHints;
+
+import com.android.internal.telecom.IVideoProvider;
+import com.android.internal.telecom.RemoteServiceCallback;
+
+/**
+ * Internal remote callback interface for connection services.
+ *
+ * @see android.telecom.ConnectionServiceAdapter
+ *
+ * {@hide}
+ */
+oneway interface IConnectionServiceAdapter {
+ void handleCreateConnectionComplete(
+ String callId,
+ in ConnectionRequest request,
+ in ParcelableConnection connection);
+
+ void setActive(String callId);
+
+ void setRinging(String callId);
+
+ void setDialing(String callId);
+
+ void setDisconnected(String callId, in DisconnectCause disconnectCause);
+
+ void setOnHold(String callId);
+
+ void setRingbackRequested(String callId, boolean ringing);
+
+ void setCallCapabilities(String callId, int callCapabilities);
+
+ void setIsConferenced(String callId, String conferenceCallId);
+
+ void addConferenceCall(String callId, in ParcelableConference conference);
+
+ void removeCall(String callId);
+
+ void onPostDialWait(String callId, String remaining);
+
+ void queryRemoteConnectionServices(RemoteServiceCallback callback);
+
+ void setVideoProvider(String callId, IVideoProvider videoProvider);
+
+ void setVideoState(String callId, int videoState);
+
+ void setIsVoipAudioMode(String callId, boolean isVoip);
+
+ void setStatusHints(String callId, in StatusHints statusHints);
+
+ void setAddress(String callId, in Uri address, int presentation);
+
+ void setCallerDisplayName(String callId, String callerDisplayName, int presentation);
+
+ void setConferenceableConnections(String callId, in List<String> conferenceableCallIds);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
new file mode 100644
index 0000000..138a877
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -0,0 +1,62 @@
+/*
+ * 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 com.android.internal.telecom;
+
+import android.telecom.PhoneAccountHandle;
+
+/**
+ * Internal remote callback interface for in-call services.
+ *
+ * @see android.telecom.InCallAdapter
+ *
+ * {@hide}
+ */
+oneway interface IInCallAdapter {
+ void answerCall(String callId, int videoState);
+
+ void rejectCall(String callId, boolean rejectWithMessage, String textMessage);
+
+ void disconnectCall(String callId);
+
+ void holdCall(String callId);
+
+ void unholdCall(String callId);
+
+ void mute(boolean shouldMute);
+
+ void setAudioRoute(int route);
+
+ void playDtmfTone(String callId, char digit);
+
+ void stopDtmfTone(String callId);
+
+ void postDialContinue(String callId, boolean proceed);
+
+ void phoneAccountSelected(String callId, in PhoneAccountHandle accountHandle);
+
+ void conference(String callId, String otherCallId);
+
+ void splitFromConference(String callId);
+
+ void mergeConference(String callId);
+
+ void swapConference(String callId);
+
+ void turnOnProximitySensor();
+
+ void turnOffProximitySensor(boolean screenOnImmediately);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
new file mode 100644
index 0000000..35f6f65
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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 com.android.internal.telecom;
+
+import android.app.PendingIntent;
+import android.telecom.AudioState;
+import android.telecom.ParcelableCall;
+
+import com.android.internal.telecom.IInCallAdapter;
+
+/**
+ * Internal remote interface for in-call services.
+ *
+ * @see android.telecom.InCallService
+ *
+ * {@hide}
+ */
+oneway interface IInCallService {
+ void setInCallAdapter(in IInCallAdapter inCallAdapter);
+
+ void addCall(in ParcelableCall call);
+
+ void updateCall(in ParcelableCall call);
+
+ void setPostDial(String callId, String remaining);
+
+ void setPostDialWait(String callId, String remaining);
+
+ void onAudioStateChanged(in AudioState audioState);
+
+ void bringToForeground(boolean showDialpad);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
new file mode 100644
index 0000000..77a80fe
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -0,0 +1,175 @@
+/*
+ * 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 com.android.internal.telecom;
+
+import android.content.ComponentName;
+import android.telecom.PhoneAccountHandle;
+import android.os.Bundle;
+import android.telecom.PhoneAccount;
+
+/**
+ * Interface used to interact with Telecom. Mostly this is used by TelephonyManager for passing
+ * commands that were previously handled by ITelephony.
+ * {@hide}
+ */
+interface ITelecomService {
+ /**
+ * Brings the in-call screen to the foreground if there is an active call.
+ *
+ * @param showDialpad if true, make the dialpad visible initially.
+ */
+ void showInCallScreen(boolean showDialpad);
+
+ /**
+ * @see TelecomServiceImpl#getDefaultOutgoingPhoneAccount
+ */
+ PhoneAccountHandle getDefaultOutgoingPhoneAccount(in String uriScheme);
+
+ /**
+ * @see TelecomServiceImpl#getUserSelectedOutgoingPhoneAccount
+ */
+ PhoneAccountHandle getUserSelectedOutgoingPhoneAccount();
+
+ /**
+ * @see TelecomServiceImpl#setUserSelectedOutgoingPhoneAccount
+ */
+ void setUserSelectedOutgoingPhoneAccount(in PhoneAccountHandle account);
+
+ /**
+ * @see TelecomServiceImpl#getCallCapablePhoneAccounts
+ */
+ List<PhoneAccountHandle> getCallCapablePhoneAccounts();
+
+ /**
+ * @see TelecomManager#getPhoneAccountsSupportingScheme
+ */
+ List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(in String uriScheme);
+
+ /**
+ * @see TelecomManager#getPhoneAccount
+ */
+ PhoneAccount getPhoneAccount(in PhoneAccountHandle account);
+
+ /**
+ * @see TelecomManager#getAllPhoneAccountsCount
+ */
+ int getAllPhoneAccountsCount();
+
+ /**
+ * @see TelecomManager#getAllPhoneAccounts
+ */
+ List<PhoneAccount> getAllPhoneAccounts();
+
+ /**
+ * @see TelecomManager#getAllPhoneAccountHandles
+ */
+ List<PhoneAccountHandle> getAllPhoneAccountHandles();
+
+ /**
+ * @see TelecomServiceImpl#getSimCallManager
+ */
+ PhoneAccountHandle getSimCallManager();
+
+ /**
+ * @see TelecomServiceImpl#setSimCallManager
+ */
+ void setSimCallManager(in PhoneAccountHandle account);
+
+ /**
+ * @see TelecomServiceImpl#getSimCallManagers
+ */
+ List<PhoneAccountHandle> getSimCallManagers();
+
+ /**
+ * @see TelecomServiceImpl#registerPhoneAccount
+ */
+ void registerPhoneAccount(in PhoneAccount metadata);
+
+ /**
+ * @see TelecomServiceImpl#unregisterPhoneAccount
+ */
+ void unregisterPhoneAccount(in PhoneAccountHandle account);
+
+ /**
+ * @see TelecomServiceImpl#clearAccounts
+ */
+ void clearAccounts(String packageName);
+
+ /**
+ * @see TelecomServiceImpl#getDefaultPhoneApp
+ */
+ ComponentName getDefaultPhoneApp();
+
+ //
+ // Internal system apis relating to call management.
+ //
+
+ /**
+ * @see TelecomServiceImpl#silenceRinger
+ */
+ void silenceRinger();
+
+ /**
+ * @see TelecomServiceImpl#isInCall
+ */
+ boolean isInCall();
+
+ /**
+ * @see TelecomServiceImpl#isRinging
+ */
+ boolean isRinging();
+
+ /**
+ * @see TelecomServiceImpl#getCallState
+ */
+ int getCallState();
+
+ /**
+ * @see TelecomServiceImpl#endCall
+ */
+ boolean endCall();
+
+ /**
+ * @see TelecomServiceImpl#acceptRingingCall
+ */
+ void acceptRingingCall();
+
+ /**
+ * @see TelecomServiceImpl#cancelMissedCallsNotification
+ */
+ void cancelMissedCallsNotification();
+
+ /**
+ * @see TelecomServiceImpl#handleMmi
+ */
+ boolean handlePinMmi(String dialString);
+
+ /**
+ * @see TelecomServiceImpl#isTtySupported
+ */
+ boolean isTtySupported();
+
+ /**
+ * @see TelecomServiceImpl#getCurrentTtyMode
+ */
+ int getCurrentTtyMode();
+
+ /**
+ * @see TelecomServiceImpl#addNewIncomingCall
+ */
+ void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IVideoCallback.aidl b/telecomm/java/com/android/internal/telecom/IVideoCallback.aidl
new file mode 100644
index 0000000..f758b60
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IVideoCallback.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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 com.android.internal.telecom;
+
+import android.telecom.CameraCapabilities;
+import android.telecom.VideoProfile;
+
+ /**
+ * Internal definition of a callback interface, used for an InCallUi to respond to video
+ * telephony changes.
+ *
+ * @see android.telecom.InCallService.VideoCall.Listener
+ *
+ * {@hide}
+ */
+oneway interface IVideoCallback {
+ void receiveSessionModifyRequest(in VideoProfile videoProfile);
+
+ void receiveSessionModifyResponse(
+ int status,
+ in VideoProfile requestedProfile,
+ in VideoProfile responseProfile);
+
+ void handleCallSessionEvent(int event);
+
+ void changePeerDimensions(int width, int height);
+
+ void changeCallDataUsage(int dataUsage);
+
+ void changeCameraCapabilities(in CameraCapabilities cameraCapabilities);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
new file mode 100644
index 0000000..e96d9d3
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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 com.android.internal.telecom;
+
+import android.view.Surface;
+import android.telecom.VideoProfile;
+
+/**
+ * Internal remote interface for a video call provider.
+ * @see android.telecom.VideoProvider
+ * @hide
+ */
+oneway interface IVideoProvider {
+ void setVideoCallback(IBinder videoCallbackBinder);
+
+ void setCamera(String cameraId);
+
+ void setPreviewSurface(in Surface surface);
+
+ void setDisplaySurface(in Surface surface);
+
+ void setDeviceOrientation(int rotation);
+
+ void setZoom(float value);
+
+ void sendSessionModifyRequest(in VideoProfile reqProfile);
+
+ void sendSessionModifyResponse(in VideoProfile responseProfile);
+
+ void requestCameraCapabilities();
+
+ void requestCallDataUsage();
+
+ void setPauseImage(String uri);
+}
diff --git a/telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl b/telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl
new file mode 100644
index 0000000..441704d
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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 com.android.internal.telecom;
+
+import android.content.ComponentName;
+
+/**
+ * Simple response callback object.
+ *
+ * {@hide}
+ */
+oneway interface RemoteServiceCallback {
+ void onError();
+ void onResult(in List<ComponentName> components, in List<IBinder> callServices);
+}