Finalize implementation of Remote Connection Service (1/4)

Change-Id: I3788c9ab03a979f22ab628637adf6d520038cb6e
This commit is contained in:
Ihab Awad
2014-07-30 10:07:40 -07:00
parent 55a684a82d
commit 5d0410fa7c
9 changed files with 866 additions and 456 deletions

View File

@@ -28596,9 +28596,9 @@ package android.telecomm {
public static class Call.Details {
method public android.telecomm.PhoneAccountHandle getAccountHandle();
method public int getCallCapabilities();
method public java.lang.String getCallerDisplayName();
method public int getCallerDisplayNamePresentation();
method public int getCapabilities();
method public long getConnectTimeMillis();
method public int getDisconnectCauseCode();
method public java.lang.String getDisconnectCauseMsg();
@@ -28616,7 +28616,6 @@ package android.telecomm {
method public void onChildrenChanged(android.telecomm.Call, java.util.List<android.telecomm.Call>);
method public void onDetailsChanged(android.telecomm.Call, android.telecomm.Call.Details);
method public void onParentChanged(android.telecomm.Call, android.telecomm.Call);
method public void onPostDial(android.telecomm.Call, java.lang.String);
method public void onPostDialWait(android.telecomm.Call, java.lang.String);
method public void onStartActivity(android.telecomm.Call, android.app.PendingIntent);
method public void onStateChanged(android.telecomm.Call, int);
@@ -28935,22 +28934,25 @@ package android.telecomm {
method public int getCallCapabilities();
method public java.lang.String getCallerDisplayName();
method public int getCallerDisplayNamePresentation();
method public int getDisconnectCause();
method public java.lang.String getDisconnectMessage();
method public java.util.List<android.telecomm.RemoteConnection> getChildren();
method public int getDisconnectCauseCode();
method public java.lang.String getDisconnectCauseMessage();
method public int getFailureCode();
method public java.lang.String getFailureMessage();
method public android.net.Uri getHandle();
method public int getHandlePresentation();
method public android.telecomm.RemoteConnection getParent();
method public int getState();
method public android.telecomm.StatusHints getStatusHints();
method public int getVideoState();
method public void hold();
method public void playDtmf(char);
method public boolean isRequestingRingback();
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
method public void reject();
method public void removeListener(android.telecomm.RemoteConnection.Listener);
method public void setAudioState(android.telecomm.CallAudioState);
method public void stopDtmf();
method public void stopDtmfTone();
method public void swapWithBackgroundCall();
method public void unhold();
}
@@ -28960,9 +28962,11 @@ package android.telecomm {
method public void onAudioModeIsVoipChanged(android.telecomm.RemoteConnection, boolean);
method public void onCallCapabilitiesChanged(android.telecomm.RemoteConnection, int);
method public void onCallerDisplayNameChanged(android.telecomm.RemoteConnection, java.lang.String, int);
method public void onChildrenChanged(android.telecomm.RemoteConnection, java.util.List<android.telecomm.RemoteConnection>);
method public void onDestroyed(android.telecomm.RemoteConnection);
method public void onDisconnected(android.telecomm.RemoteConnection, int, java.lang.String);
method public void onHandleChanged(android.telecomm.RemoteConnection, android.net.Uri, int);
method public void onParentChanged(android.telecomm.RemoteConnection, android.telecomm.RemoteConnection);
method public void onPostDialWait(android.telecomm.RemoteConnection, java.lang.String);
method public void onRequestingRingback(android.telecomm.RemoteConnection, boolean);
method public void onStartActivityFromInCall(android.telecomm.RemoteConnection, android.app.PendingIntent);
@@ -43207,8 +43211,8 @@ package java.lang.reflect {
method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public boolean isAccessible();
method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean);
method public void setAccessible(boolean);
method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean);
}
public abstract interface AnnotatedElement {
@@ -43242,6 +43246,11 @@ package java.lang.reflect {
method public static void setShort(java.lang.Object, int, short) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
}
public final class ArtMethod {
method public int getAccessFlags();
method public static java.lang.String getMethodName(java.lang.reflect.ArtMethod);
}
public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
method public A getAnnotation(java.lang.Class<A>);
method public java.lang.Class<T> getDeclaringClass();

View File

@@ -74,7 +74,7 @@ public final class Call {
private final String mCallerDisplayName;
private final int mCallerDisplayNamePresentation;
private final PhoneAccountHandle mAccountHandle;
private final int mCapabilities;
private final int mCallCapabilities;
private final int mDisconnectCauseCode;
private final String mDisconnectCauseMsg;
private final long mConnectTimeMillis;
@@ -125,8 +125,8 @@ public final class Call {
* @return A bitmask of the capabilities of the {@code Call}, as defined in
* {@link CallCapabilities}.
*/
public int getCapabilities() {
return mCapabilities;
public int getCallCapabilities() {
return mCallCapabilities;
}
/**
@@ -162,14 +162,15 @@ public final class Call {
}
/**
* @return Returns the video state of the {@code Call}.
* @return The video state of the {@code Call}.
*/
public int getVideoState() {
return mVideoState;
}
/*
* @return The current {@link android.telecomm.StatusHints}, or null if none has been set.
/**
* @return The current {@link android.telecomm.StatusHints}, or {@code null} if none
* have been set.
*/
public StatusHints getStatusHints() {
return mStatusHints;
@@ -186,7 +187,7 @@ public final class Call {
Objects.equals(mCallerDisplayNamePresentation,
d.mCallerDisplayNamePresentation) &&
Objects.equals(mAccountHandle, d.mAccountHandle) &&
Objects.equals(mCapabilities, d.mCapabilities) &&
Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) &&
Objects.equals(mDisconnectCauseMsg, d.mDisconnectCauseMsg) &&
Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
@@ -205,7 +206,7 @@ public final class Call {
Objects.hashCode(mCallerDisplayName) +
Objects.hashCode(mCallerDisplayNamePresentation) +
Objects.hashCode(mAccountHandle) +
Objects.hashCode(mCapabilities) +
Objects.hashCode(mCallCapabilities) +
Objects.hashCode(mDisconnectCauseCode) +
Objects.hashCode(mDisconnectCauseMsg) +
Objects.hashCode(mConnectTimeMillis) +
@@ -233,7 +234,7 @@ public final class Call {
mCallerDisplayName = callerDisplayName;
mCallerDisplayNamePresentation = callerDisplayNamePresentation;
mAccountHandle = accountHandle;
mCapabilities = capabilities;
mCallCapabilities = capabilities;
mDisconnectCauseCode = disconnectCauseCode;
mDisconnectCauseMsg = disconnectCauseMsg;
mConnectTimeMillis = connectTimeMillis;
@@ -288,15 +289,6 @@ public final class Call {
*/
public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
/**
* Invoked when the outgoing {@code Call} has finished dialing but is sending DTMF signals
* that were embedded into the outgoing number.
*
* @param call The {@code Call} invoking this method.
* @param remainingPostDialSequence The post-dial characters that remain to be sent.
*/
public void onPostDial(Call call, String remainingPostDialSequence) {}
/**
* 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
@@ -314,7 +306,6 @@ public final class Call {
* @param call The {@code Call} invoking this method.
* @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
*/
public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
/**
@@ -427,8 +418,6 @@ public final class Call {
*
* 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, this {@code Call} will notify listeners via
* {@link Listener#onPostDial(Call, String)}.
*
* If the DTMF string contains a {@link TelecommManager#DTMF_CHARACTER_PAUSE} symbol, this
* {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
@@ -656,12 +645,6 @@ public final class Call {
}
}
/** {@hide} */
final void internalSetPostDial(String remaining) {
mRemainingPostDialSequence = remaining;
firePostDial(mRemainingPostDialSequence);
}
/** {@hide} */
final void internalSetPostDialWait(String remaining) {
mRemainingPostDialSequence = remaining;
@@ -715,13 +698,6 @@ public final class Call {
}
}
private void firePostDial(String remainingPostDialSequence) {
Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
for (int i = 0; i < listeners.length; i++) {
listeners[i].onPostDial(this, remainingPostDialSequence);
}
}
private void firePostDialWait(String remainingPostDialSequence) {
Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
for (int i = 0; i < listeners.length; i++) {

View File

@@ -39,6 +39,7 @@ import com.android.internal.telecomm.IVideoCallCallback;
import com.android.internal.telecomm.IVideoCallProvider;
import com.android.internal.telecomm.RemoteServiceCallback;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -81,6 +82,7 @@ public abstract class ConnectionService extends Service {
private final RemoteConnectionManager mRemoteConnectionManager = new RemoteConnectionManager();
private boolean mAreAccountsInitialized = false;
private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>();
private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
/**
@@ -226,11 +228,23 @@ public abstract class ConnectionService extends Service {
case MSG_CREATE_CONNECTION: {
SomeArgs args = (SomeArgs) msg.obj;
try {
PhoneAccountHandle connectionManagerPhoneAccount =
final PhoneAccountHandle connectionManagerPhoneAccount =
(PhoneAccountHandle) args.arg1;
ConnectionRequest request = (ConnectionRequest) args.arg2;
boolean isIncoming = args.argi1 == 1;
createConnection(connectionManagerPhoneAccount, request, isIncoming);
final ConnectionRequest request = (ConnectionRequest) args.arg2;
final boolean isIncoming = args.argi1 == 1;
if (!mAreAccountsInitialized) {
mPreInitializationConnectionRequests.add(new Runnable() {
@Override
public void run() {
createConnection(
connectionManagerPhoneAccount,
request,
isIncoming);
}
});
} else {
createConnection(connectionManagerPhoneAccount, request, isIncoming);
}
} finally {
args.recycle();
}
@@ -643,7 +657,7 @@ public abstract class ConnectionService extends Service {
componentNames.get(i),
IConnectionService.Stub.asInterface(services.get(i)));
}
mAreAccountsInitialized = true;
onAccountsInitialized();
Log.d(this, "remote connection services found: " + services);
}
});
@@ -803,6 +817,14 @@ public abstract class ConnectionService extends Service {
return builder.toString();
}
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);

View File

@@ -0,0 +1,377 @@
/*
* 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.telecomm;
import com.android.internal.os.SomeArgs;
import com.android.internal.telecomm.IConnectionServiceAdapter;
import com.android.internal.telecomm.IVideoCallProvider;
import com.android.internal.telecomm.RemoteServiceCallback;
import android.app.PendingIntent;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
/**
* 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_SUCCESSFUL = 1;
private static final int MSG_HANDLE_CREATE_CONNECTION_FAILED = 2;
private static final int MSG_HANDLE_CREATE_CONNECTION_CANCELLED = 3;
private static final int MSG_SET_ACTIVE = 4;
private static final int MSG_SET_RINGING = 5;
private static final int MSG_SET_DIALING = 6;
private static final int MSG_SET_DISCONNECTED = 7;
private static final int MSG_SET_ON_HOLD = 8;
private static final int MSG_SET_REQUESTING_RINGBACK = 9;
private static final int MSG_SET_CALL_CAPABILITIES = 10;
private static final int MSG_SET_IS_CONFERENCED = 11;
private static final int MSG_ADD_CONFERENCE_CALL = 12;
private static final int MSG_REMOVE_CALL = 13;
private static final int MSG_ON_POST_DIAL_WAIT = 14;
private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 15;
private static final int MSG_SET_VIDEO_STATE = 16;
private static final int MSG_SET_VIDEO_CALL_PROVIDER = 17;
private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 18;
private static final int MSG_SET_STATUS_HINTS = 19;
private static final int MSG_SET_HANDLE = 20;
private static final int MSG_SET_CALLER_DISPLAY_NAME = 21;
private static final int MSG_START_ACTIVITY_FROM_IN_CALL = 22;
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_SUCCESSFUL: {
SomeArgs args = (SomeArgs) msg.obj;
try {
mDelegate.handleCreateConnectionSuccessful(
(ConnectionRequest) args.arg1,
(ParcelableConnection) args.arg2);
} finally {
args.recycle();
}
break;
}
case MSG_HANDLE_CREATE_CONNECTION_FAILED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
mDelegate.handleCreateConnectionFailed(
(ConnectionRequest) args.arg1,
args.argi1,
(String) args.arg2);
} finally {
args.recycle();
}
break;
}
case MSG_HANDLE_CREATE_CONNECTION_CANCELLED: {
mDelegate.handleCreateConnectionCancelled((ConnectionRequest) msg.obj);
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, args.argi1, (String) args.arg2);
} finally {
args.recycle();
}
break;
}
case MSG_SET_ON_HOLD:
mDelegate.setOnHold((String) msg.obj);
break;
case MSG_SET_REQUESTING_RINGBACK:
mDelegate.setRequestingRingback((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:
mDelegate.addConferenceCall((String) msg.obj);
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.setVideoCallProvider((String) args.arg1,
(IVideoCallProvider) args.arg2);
} finally {
args.recycle();
}
break;
}
case MSG_SET_AUDIO_MODE_IS_VOIP:
mDelegate.setAudioModeIsVoip((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_HANDLE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
mDelegate.setHandle((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_START_ACTIVITY_FROM_IN_CALL: {
SomeArgs args = (SomeArgs) msg.obj;
try {
mDelegate.startActivityFromInCall(
(String) args.arg1, (PendingIntent) args.arg2);
} finally {
args.recycle();
}
break;
}
}
}
};
private final IConnectionServiceAdapter mStub = new IConnectionServiceAdapter.Stub() {
@Override
public void handleCreateConnectionSuccessful(
ConnectionRequest request, ParcelableConnection connection) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = request;
args.arg2 = connection;
mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, args).sendToTarget();
}
@Override
public void handleCreateConnectionFailed(
ConnectionRequest request, int errorCode, String errorMessage) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = request;
args.argi1 = errorCode;
args.arg2 = errorMessage;
mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_FAILED, args).sendToTarget();
}
@Override
public void handleCreateConnectionCancelled(ConnectionRequest request) {
mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_CANCELLED, request).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, int disconnectCause, String disconnectMessage) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = disconnectMessage;
args.argi1 = 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 setRequestingRingback(String connectionId, boolean ringback) {
mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, 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) {
mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, callId).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 setVideoCallProvider(
String connectionId, IVideoCallProvider videoCallProvider) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = videoCallProvider;
mHandler.obtainMessage(MSG_SET_VIDEO_CALL_PROVIDER, args).sendToTarget();
}
@Override
public final void setAudioModeIsVoip(String connectionId, boolean isVoip) {
mHandler.obtainMessage(MSG_SET_AUDIO_MODE_IS_VOIP, 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 setHandle(String connectionId, Uri handle, int presentation) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = handle;
args.argi1 = presentation;
mHandler.obtainMessage(MSG_SET_HANDLE, 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 startActivityFromInCall(String connectionId, PendingIntent intent) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = intent;
mHandler.obtainMessage(MSG_START_ACTIVITY_FROM_IN_CALL, args).sendToTarget();
}
};
public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
mDelegate = delegate;
}
public IConnectionServiceAdapter getStub() {
return mStub;
}
}

View File

@@ -41,11 +41,10 @@ public abstract class InCallService extends Service {
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 = 4;
private static final int MSG_SET_POST_DIAL_WAIT = 5;
private static final int MSG_ON_AUDIO_STATE_CHANGED = 6;
private static final int MSG_BRING_TO_FOREGROUND = 7;
private static final int MSG_START_ACTIVITY = 8;
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;
private static final int MSG_START_ACTIVITY = 7;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -62,17 +61,6 @@ public abstract class InCallService extends Service {
case MSG_UPDATE_CALL:
mPhone.internalUpdateCall((ParcelableCall) msg.obj);
break;
case MSG_SET_POST_DIAL: {
SomeArgs args = (SomeArgs) msg.obj;
try {
String callId = (String) args.arg1;
String remaining = (String) args.arg2;
mPhone.internalSetPostDial(callId, remaining);
} finally {
args.recycle();
}
break;
}
case MSG_SET_POST_DIAL_WAIT: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -124,10 +112,7 @@ public abstract class InCallService extends Service {
@Override
public void setPostDial(String callId, String remaining) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
args.arg2 = remaining;
mHandler.obtainMessage(MSG_SET_POST_DIAL, args).sendToTarget();
// TODO: Unused
}
@Override

View File

@@ -120,14 +120,6 @@ public final class Phone {
}
}
/** {@hide} */
final void internalSetPostDial(String telecommId, String remaining) {
Call call = mCallByTelecommCallId.get(telecommId);
if (call != null) {
call.internalSetPostDial(remaining);
}
}
/** {@hide} */
final void internalSetPostDialWait(String telecommId, String remaining) {
Call call = mCallByTelecommCallId.get(telecommId);

View File

@@ -24,25 +24,152 @@ import android.telephony.DisconnectCause;
import com.android.internal.telecomm.IConnectionService;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* RemoteConnection object used by RemoteConnectionService.
*/
public final class RemoteConnection {
public static abstract class Listener {
/**
* 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) {}
public void onDisconnected(RemoteConnection connection, int cause, String message) {}
/**
* Invoked when the parent of this {@code RemoteConnection} has changed. See
* {@link #getParent()}.
*
* @param connection The {@code RemoteConnection} invoking this method.
* @param parent The new parent of the {@code RemoteConnection}.
*/
public void onParentChanged(RemoteConnection connection, RemoteConnection parent) {}
/**
* Invoked when the children of this {@code RemoteConnection} have changed. See
* {@link #getChildren()}.
*
* @param connection The {@code RemoteConnection} invoking this method.
* @param children The new children of the {@code RemoteConnection}.
*/
public void onChildrenChanged(
RemoteConnection connection, List<RemoteConnection> children) {}
/**
* Invoked when this {@code RemoteConnection} is disconnected.
*
* @param connection The {@code RemoteConnection} invoking this method.
* @param disconnectCauseCode The failure code ({@see DisconnectCause}) associated with this
* failed connection.
* @param disconnectCauseMessage The reason for the connection failure. This will not be
* displayed to the user.
*/
public void onDisconnected(
RemoteConnection connection,
int disconnectCauseCode,
String disconnectCauseMessage) {}
/**
* Invoked when this {@code RemoteConnection} is requesting ringback. See
* {@link #isRequestingRingback()}.
*
* @param connection The {@code RemoteConnection} invoking this method.
* @param ringback Whether the {@code RemoteConnection} is requesting ringback.
*/
public void onRequestingRingback(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) {}
public void onPostDialWait(RemoteConnection connection, String remainingDigits) {}
/**
* 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 #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 #getAudioModeIsVoip()}.
*
* @param connection The {@code RemoteConnection} invoking this method.
* @param isVoip Whether the new audio state of the {@code RemoteConnection} is VOIP.
*/
public void onAudioModeIsVoipChanged(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 handle (e.g., phone number) of this {@code RemoteConnection} has
* changed. See {@link #getHandle()} and {@link #getHandlePresentation()}.
*
* @param connection The {@code RemoteConnection} invoking this method.
* @param handle The new handle of the {@code RemoteConnection}.
* @param presentation The {@link CallPropertyPresentation} which controls how the
* handle is shown.
*/
public void onHandleChanged(RemoteConnection connection, Uri handle, 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 {@link CallPropertyPresentation} which controls how the
* caller display name is shown.
*/
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}.
*/
public void onVideoStateChanged(RemoteConnection connection, int videoState) {}
/**
* Indicates that this {@code RemoteConnection} is requesting that the in-call UI
* launch the specified activity.
*
* @param connection The {@code RemoteConnection} invoking this method.
* @param intent A {@link PendingIntent} that the {@code RemoteConnection} wishes to
* have launched on its behalf.
*/
public void onStartActivityFromInCall(RemoteConnection connection, PendingIntent intent) {}
/**
* 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) {}
}
@@ -51,8 +178,8 @@ public final class RemoteConnection {
private final Set<Listener> mListeners = new HashSet<>();
private int mState = Connection.State.NEW;
private int mDisconnectCause = DisconnectCause.NOT_VALID;
private String mDisconnectMessage;
private int mDisconnectCauseCode = DisconnectCause.NOT_VALID;
private String mDisconnectCauseMessage;
private boolean mRequestingRingback;
private boolean mConnected;
private int mCallCapabilities;
@@ -69,10 +196,9 @@ public final class RemoteConnection {
/**
* @hide
*/
RemoteConnection(IConnectionService connectionService, ConnectionRequest request,
boolean isIncoming) {
RemoteConnection(IConnectionService connectionService, ConnectionRequest request) {
mConnectionService = connectionService;
mConnectionId = request.getCallId();
mConnectionId = request == null ? "NULL" : request.getCallId();
mConnected = true;
mState = Connection.State.INITIALIZING;
@@ -87,73 +213,161 @@ public final class RemoteConnection {
* @param failureMessage
*/
private RemoteConnection(int failureCode, String failureMessage) {
this(null, null, true);
this(null, null);
mConnected = false;
mState = Connection.State.FAILED;
mFailureCode = failureCode;
mFailureMessage = failureMessage;
}
/**
* Adds a listener to this {@code RemoteConnection}.
*
* @param listener A {@code Listener}.
*/
public void addListener(Listener listener) {
mListeners.add(listener);
}
/**
* Removes a listener from this {@code RemoteConnection}.
*
* @param listener A {@code Listener}.
*/
public void removeListener(Listener listener) {
mListeners.remove(listener);
}
/**
* Obtains the parent of this {@code RemoteConnection} in a conference, if any.
*
* @return The parent {@code RemoteConnection}, or {@code null} if this {@code RemoteConnection}
* is not a child of any conference {@code RemoteConnection}s.
*/
public RemoteConnection getParent() { return null; }
/**
* Obtains the children of this conference {@code RemoteConnection}, if any.
*
* @return The children of this {@code RemoteConnection} if this {@code RemoteConnection} is
* a conference, or an empty {@code List} otherwise.
*/
public List<RemoteConnection> getChildren() { return null; }
/**
* Obtains the state of this {@code RemoteConnection}.
*
* @return A state value, chosen from the {@code STATE_*} constants.
*/
public int getState() {
return mState;
}
public int getDisconnectCause() {
return mDisconnectCause;
/**
* @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 int getDisconnectCauseCode() {
return mDisconnectCauseCode;
}
public String getDisconnectMessage() {
return mDisconnectMessage;
/**
* @return For a {@link Connection.State#DISCONNECTED} {@code RemoteConnection}, an optional
* reason for disconnection expressed as a free text message.
*/
public String getDisconnectCauseMessage() {
return mDisconnectCauseMessage;
}
/**
* @return A bitmask of the capabilities of the {@code RemoteConnection}, as defined in
* {@link CallCapabilities}.
*/
public int getCallCapabilities() {
return mCallCapabilities;
}
/**
* @return {@code true} if the {@code RemoteConnection}'s current audio mode is VOIP.
*/
public boolean getAudioModeIsVoip() {
return mAudioModeIsVoip;
}
/**
* @return The current {@link android.telecomm.StatusHints} of this {@code RemoteConnection},
* or {@code null} if none have been set.
*/
public StatusHints getStatusHints() {
return mStatusHints;
}
/**
* @return The handle (e.g., phone number) to which the {@code RemoteConnection} is currently
* connected.
*/
public Uri getHandle() {
return mHandle;
}
/**
* @return The presentation requirements for the handle. See
* {@link android.telecomm.CallPropertyPresentation} 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 android.telecomm.CallPropertyPresentation} for valid values.
*/
public int getCallerDisplayNamePresentation() {
return mCallerDisplayNamePresentation;
}
/**
* @return The video state of the {@code RemoteConnection}. See
* {@link VideoCallProfile.VideoState}.
*/
public int getVideoState() {
return mVideoState;
}
/**
* @return The failure code ({@see DisconnectCause}) associated with this failed
* {@code RemoteConnection}.
*/
public int getFailureCode() {
return mFailureCode;
}
/**
* @return The reason for the connection failure. This will not be displayed to the user.
*/
public String getFailureMessage() {
return mFailureMessage;
}
/**
* @return Whether the {@code RemoteConnection} is requesting that the framework play a
* ringback tone on its behalf.
*/
public boolean isRequestingRingback() {
return false;
}
/**
* Instructs this {@code RemoteConnection} to abort.
*/
public void abort() {
try {
if (mConnected) {
@@ -163,6 +377,10 @@ public final class RemoteConnection {
}
}
/**
* Instructs this {@link Connection.State#RINGING} {@code RemoteConnection} to answer.
* @param videoState The video state in which to answer the call.
*/
public void answer(int videoState) {
try {
if (mConnected) {
@@ -172,6 +390,9 @@ public final class RemoteConnection {
}
}
/**
* Instructs this {@link Connection.State#RINGING} {@code RemoteConnection} to reject.
*/
public void reject() {
try {
if (mConnected) {
@@ -181,6 +402,9 @@ public final class RemoteConnection {
}
}
/**
* Instructs this {@code RemoteConnection} to go on hold.
*/
public void hold() {
try {
if (mConnected) {
@@ -190,6 +414,9 @@ public final class RemoteConnection {
}
}
/**
* Instructs this {@link Connection.State#HOLDING} call to release from hold.
*/
public void unhold() {
try {
if (mConnected) {
@@ -199,6 +426,9 @@ public final class RemoteConnection {
}
}
/**
* Instructs this {@code RemoteConnection} to disconnect.
*/
public void disconnect() {
try {
if (mConnected) {
@@ -208,7 +438,16 @@ public final class RemoteConnection {
}
}
public void playDtmf(char digit) {
/**
* 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);
@@ -217,7 +456,14 @@ public final class RemoteConnection {
}
}
public void stopDtmf() {
/**
* 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);
@@ -226,6 +472,27 @@ public final class RemoteConnection {
}
}
/**
* 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 TelecommManager#DTMF_CHARACTER_WAIT} or {@link TelecommManager#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 TelecommManager#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 TelecommManager#DTMF_CHARACTER_WAIT} symbol, this
* {@code RemoteConnection} will pause playing the tones and notify listeners via
* {@link Listener#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) {
@@ -235,6 +502,10 @@ public final class RemoteConnection {
}
}
/**
* Instructs this {@code RemoteConnection} to swap itself with an existing background call,
* if one such call exists.
*/
public void swapWithBackgroundCall() {
try {
if (mConnected) {
@@ -244,6 +515,11 @@ public final class RemoteConnection {
}
}
/**
* Set the audio state of this {@code RemoteConnection}.
*
* @param state The audio state of this {@code RemoteConnection}.
*/
public void setAudioState(CallAudioState state) {
try {
if (mConnected) {
@@ -271,8 +547,8 @@ public final class RemoteConnection {
void setDisconnected(int cause, String message) {
if (mState != Connection.State.DISCONNECTED) {
mState = Connection.State.DISCONNECTED;
mDisconnectCause = cause;
mDisconnectMessage = message;
mDisconnectCauseCode = cause;
mDisconnectCauseMessage = message;
for (Listener l : mListeners) {
l.onDisconnected(this, cause, message);
@@ -382,13 +658,6 @@ public final class RemoteConnection {
}
}
/** @hide */
void setConnectionService(IConnectionService connectionService) {
mConnectionService = connectionService;
mConnectionService = null;
setState(Connection.State.NEW);
}
/**
* Create a RemoteConnection which is in the {@link Connection.State#FAILED} state. Attempting
* to use it for anything will almost certainly result in bad things happening. Do not do this.
@@ -396,7 +665,6 @@ public final class RemoteConnection {
* @return a failed {@link RemoteConnection}
*
* @hide
*
*/
public static RemoteConnection failure(int failureCode, String failureMessage) {
return new RemoteConnection(failureCode, failureMessage);

View File

@@ -17,14 +17,11 @@
package android.telecomm;
import android.content.ComponentName;
import android.net.Uri;
import android.os.RemoteException;
import com.android.internal.telecomm.IConnectionService;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
@@ -37,18 +34,13 @@ public class RemoteConnectionManager {
if (!mRemoteConnectionServices.containsKey(componentName)) {
try {
RemoteConnectionService remoteConnectionService =
new RemoteConnectionService(componentName, connectionService);
new RemoteConnectionService(connectionService);
mRemoteConnectionServices.put(componentName, remoteConnectionService);
} catch (RemoteException ignored) {
}
}
}
List<PhoneAccountHandle> getAccounts(Uri handle) {
List<PhoneAccountHandle> accountHandles = new LinkedList<>();
return accountHandles;
}
public RemoteConnection createRemoteConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
ConnectionRequest request,

View File

@@ -17,21 +17,21 @@
package android.telecomm;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.DisconnectCause;
import android.text.TextUtils;
import com.android.internal.os.SomeArgs;
import com.android.internal.telecomm.IConnectionService;
import com.android.internal.telecomm.IConnectionServiceAdapter;
import com.android.internal.telecomm.IVideoCallProvider;
import com.android.internal.telecomm.RemoteServiceCallback;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
@@ -39,369 +39,187 @@ import java.util.UUID;
*
* @hide
*/
final class RemoteConnectionService implements DeathRecipient {
private static final int MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL = 1;
private static final int MSG_HANDLE_CREATE_CONNECTION_FAILED = 2;
private static final int MSG_HANDLE_CREATE_CONNECTION_CANCELLED = 3;
private static final int MSG_SET_ACTIVE = 4;
private static final int MSG_SET_RINGING = 5;
private static final int MSG_SET_DIALING = 6;
private static final int MSG_SET_DISCONNECTED = 7;
private static final int MSG_SET_ON_HOLD = 8;
private static final int MSG_SET_REQUESTING_RINGBACK = 9;
private static final int MSG_SET_CALL_CAPABILITIES = 10;
private static final int MSG_SET_IS_CONFERENCED = 11;
private static final int MSG_ADD_CONFERENCE_CALL = 12;
private static final int MSG_REMOVE_CALL = 13;
private static final int MSG_ON_POST_DIAL_WAIT = 14;
private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 15;
private static final int MSG_SET_VIDEO_STATE = 16;
private static final int MSG_SET_CALL_VIDEO_PROVIDER = 17;
private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 18;
private static final int MSG_SET_STATUS_HINTS = 19;
private static final int MSG_SET_HANDLE = 20;
private static final int MSG_SET_CALLER_DISPLAY_NAME = 21;
private static final int MSG_START_ACTIVITY_FROM_IN_CALL = 22;
final class RemoteConnectionService {
private final IConnectionService mConnectionService;
private final ComponentName mComponentName;
private static final RemoteConnection NULL_CONNECTION = new RemoteConnection(null, null);
private String mConnectionId;
// Remote connection services only support a single connection.
private RemoteConnection mConnection;
private final Handler mHandler = new Handler() {
private final IConnectionServiceAdapter mServantDelegate = new IConnectionServiceAdapter() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL: {
SomeArgs args = (SomeArgs) msg.obj;
try {
ConnectionRequest request = (ConnectionRequest) args.arg1;
if (isPendingConnection(request.getCallId())) {
ParcelableConnection parcel = (ParcelableConnection) args.arg2;
mConnection.setState(parcel.getState());
mConnection.setCallCapabilities(parcel.getCapabilities());
mConnection.setHandle(
parcel.getHandle(), parcel.getHandlePresentation());
mConnection.setCallerDisplayName(
parcel.getCallerDisplayName(),
parcel.getCallerDisplayNamePresentation());
// TODO: Do we need to support video providers for remote connections?
}
} finally {
args.recycle();
}
break;
}
case MSG_HANDLE_CREATE_CONNECTION_FAILED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
ConnectionRequest request = (ConnectionRequest) args.arg1;
if (isPendingConnection(request.getCallId())) {
// TODO: How do we propogate the failure codes?
destroyConnection();
}
} finally {
args.recycle();
}
break;
}
case MSG_HANDLE_CREATE_CONNECTION_CANCELLED: {
ConnectionRequest request = (ConnectionRequest) msg.obj;
if (isPendingConnection(request.getCallId())) {
destroyConnection();
}
break;
}
case MSG_SET_ACTIVE:
if (isCurrentConnection(msg.obj)) {
mConnection.setState(Connection.State.ACTIVE);
}
break;
case MSG_SET_RINGING:
if (isCurrentConnection(msg.obj)) {
mConnection.setState(Connection.State.RINGING);
}
break;
case MSG_SET_DIALING:
if (isCurrentConnection(msg.obj)) {
mConnection.setState(Connection.State.DIALING);
}
break;
case MSG_SET_DISCONNECTED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
if (isCurrentConnection(args.arg1)) {
mConnection.setDisconnected(args.argi1, (String) args.arg2);
}
} finally {
args.recycle();
}
break;
}
case MSG_SET_ON_HOLD:
if (isCurrentConnection(msg.obj)) {
mConnection.setState(Connection.State.HOLDING);
}
break;
case MSG_SET_REQUESTING_RINGBACK:
if (isCurrentConnection(msg.obj)) {
mConnection.setRequestingRingback(msg.arg1 == 1);
}
break;
case MSG_SET_CALL_CAPABILITIES:
if (isCurrentConnection(msg.obj)) {
mConnection.setCallCapabilities(msg.arg1);
}
break;
case MSG_SET_IS_CONFERENCED:
// not supported for remote connections.
break;
case MSG_ADD_CONFERENCE_CALL:
// not supported for remote connections.
break;
case MSG_REMOVE_CALL:
if (isCurrentConnection(msg.obj)) {
destroyConnection();
}
break;
case MSG_ON_POST_DIAL_WAIT: {
SomeArgs args = (SomeArgs) msg.obj;
try {
if (isCurrentConnection(args.arg1)) {
mConnection.setPostDialWait((String) args.arg2);
}
} finally {
args.recycle();
}
break;
}
case MSG_QUERY_REMOTE_CALL_SERVICES:
// Not supported from remote connection service.
break;
case MSG_SET_VIDEO_STATE:
if (isCurrentConnection(msg.obj)) {
mConnection.setVideoState(msg.arg1);
}
break;
case MSG_SET_CALL_VIDEO_PROVIDER:
// not supported for remote connections.
break;
case MSG_SET_AUDIO_MODE_IS_VOIP:
if (isCurrentConnection(msg.obj)) {
mConnection.setAudioModeIsVoip(msg.arg1 == 1);
}
break;
case MSG_SET_STATUS_HINTS: {
SomeArgs args = (SomeArgs) msg.obj;
try {
if (isCurrentConnection(args.arg1)) {
mConnection.setStatusHints((StatusHints) args.arg2);
}
} finally {
args.recycle();
}
break;
}
case MSG_SET_HANDLE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
if (isCurrentConnection(args.arg1)) {
mConnection.setHandle((Uri) args.arg2, args.argi1);
}
} finally {
args.recycle();
}
break;
}
case MSG_SET_CALLER_DISPLAY_NAME: {
SomeArgs args = (SomeArgs) msg.obj;
try {
if (isCurrentConnection(msg.arg1)) {
mConnection.setCallerDisplayName((String) args.arg2, args.argi1);
}
} finally {
args.recycle();
}
break;
}
case MSG_START_ACTIVITY_FROM_IN_CALL: {
SomeArgs args = (SomeArgs) msg.obj;
try {
if (isCurrentConnection(msg.arg1)) {
mConnection.startActivityFromInCall((PendingIntent) args.arg2);
}
} finally {
args.recycle();
}
break;
}
public void handleCreateConnectionSuccessful(ConnectionRequest request,
ParcelableConnection parcel) {
RemoteConnection connection = findConnectionForAction(
request.getCallId(), "handleCreateConnectionSuccessful");
if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) {
mPendingConnections.remove(connection);
connection.setState(parcel.getState());
connection.setCallCapabilities(parcel.getCapabilities());
connection.setHandle(
parcel.getHandle(), parcel.getHandlePresentation());
connection.setCallerDisplayName(
parcel.getCallerDisplayName(),
parcel.getCallerDisplayNamePresentation());
// TODO: Do we need to support video providers for remote connections?
}
}
};
private final IConnectionServiceAdapter mAdapter = new IConnectionServiceAdapter.Stub() {
@Override
public void handleCreateConnectionSuccessful(
ConnectionRequest request, ParcelableConnection connection) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = request;
args.arg2 = connection;
mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, args).sendToTarget();
}
@Override
public void handleCreateConnectionFailed(
ConnectionRequest request, int errorCode, String errorMessage) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = request;
args.argi1 = errorCode;
args.arg2 = errorMessage;
mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_FAILED, args).sendToTarget();
public void handleCreateConnectionFailed(ConnectionRequest request, int errorCode,
String errorMessage) {
// TODO: How do we propagate the failure codes?
findConnectionForAction(
request.getCallId(), "handleCreateConnectionFailed")
.setDestroyed();
}
@Override
public void handleCreateConnectionCancelled(ConnectionRequest request) {
mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_CANCELLED, request).sendToTarget();
findConnectionForAction(
request.getCallId(), "handleCreateConnectionCancelled")
.setDestroyed();
}
@Override
public void setActive(String connectionId) {
mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget();
public void setActive(String callId) {
findConnectionForAction(callId, "setActive")
.setState(Connection.State.ACTIVE);
}
@Override
public void setRinging(String connectionId) {
mHandler.obtainMessage(MSG_SET_RINGING, connectionId).sendToTarget();
public void setRinging(String callId) {
findConnectionForAction(callId, "setRinging")
.setState(Connection.State.RINGING);
}
@Override
public void setDialing(String connectionId) {
mHandler.obtainMessage(MSG_SET_DIALING, connectionId).sendToTarget();
public void setDialing(String callId) {
findConnectionForAction(callId, "setDialing")
.setState(Connection.State.DIALING);
}
@Override
public void setDisconnected(
String connectionId, int disconnectCause, String disconnectMessage) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = disconnectMessage;
args.argi1 = disconnectCause;
mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
public void setDisconnected(String callId, int disconnectCause,
String disconnectMessage) {
findConnectionForAction(callId, "setDisconnected")
.setDisconnected(disconnectCause, disconnectMessage);
}
@Override
public void setOnHold(String connectionId) {
mHandler.obtainMessage(MSG_SET_ON_HOLD, connectionId).sendToTarget();
public void setOnHold(String callId) {
findConnectionForAction(callId, "setOnHold")
.setState(Connection.State.HOLDING);
}
@Override
public void setRequestingRingback(String connectionId, boolean ringback) {
mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, ringback ? 1 : 0, 0, connectionId)
.sendToTarget();
public void setRequestingRingback(String callId, boolean ringing) {
findConnectionForAction(callId, "setRequestingRingback")
.setRequestingRingback(ringing);
}
@Override
public void setCallCapabilities(String connectionId, int callCapabilities) {
mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, callCapabilities, 0, connectionId)
.sendToTarget();
public void setCallCapabilities(String callId, int callCapabilities) {
findConnectionForAction("callId", "setCallCapabilities")
.setCallCapabilities(callCapabilities);
}
@Override
public void setIsConferenced(String connectionId, String conferenceConnectionId) {
public void setIsConferenced(String callId, String conferenceCallId) {
// not supported for remote connections.
}
@Override
public void addConferenceCall(String connectionId) {
public void addConferenceCall(String callId) {
// not supported for remote connections.
}
@Override
public void removeCall(String connectionId) {
mHandler.obtainMessage(MSG_REMOVE_CALL, connectionId).sendToTarget();
public void removeCall(String callId) {
findConnectionForAction(callId, "removeCall")
.setDestroyed();
}
@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();
public void onPostDialWait(String callId, String remaining) {
findConnectionForAction(callId, "onPostDialWait")
.setPostDialWait(remaining);
}
@Override
public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
try {
// Not supported from remote connection service.
callback.onError();
} catch (RemoteException e) {
}
// Not supported from remote connection service.
}
@Override
public void setVideoState(String connectionId, int videoState) {
mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, connectionId).sendToTarget();
}
@Override
public void setVideoCallProvider(
String connectionId, IVideoCallProvider videoCallProvider) {
public void setVideoCallProvider(String callId,
IVideoCallProvider videoCallProvider) {
// not supported for remote connections.
}
@Override
public final void setAudioModeIsVoip(String connectionId, boolean isVoip) {
mHandler.obtainMessage(MSG_SET_AUDIO_MODE_IS_VOIP, isVoip ? 1 : 0, 0,
connectionId).sendToTarget();
public void setVideoState(String callId, int videoState) {
findConnectionForAction(callId, "setVideoState")
.setVideoState(videoState);
}
@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();
public void setAudioModeIsVoip(String callId, boolean isVoip) {
findConnectionForAction(callId, "setAudioModeIsVoip")
.setAudioModeIsVoip(isVoip);
}
@Override
public final void setHandle(String connectionId, Uri handle, int presentation) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = handle;
args.argi1 = presentation;
mHandler.obtainMessage(MSG_SET_HANDLE, args).sendToTarget();
public void setStatusHints(String callId, StatusHints statusHints) {
findConnectionForAction(callId, "setStatusHints")
.setStatusHints(statusHints);
}
@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();
public void setHandle(String callId, Uri handle, int presentation) {
findConnectionForAction(callId, "setHandle")
.setHandle(handle, presentation);
}
@Override
public final void startActivityFromInCall(String connectionId, PendingIntent intent) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = intent;
mHandler.obtainMessage(MSG_START_ACTIVITY_FROM_IN_CALL, args).sendToTarget();
public void setCallerDisplayName(String callId, String callerDisplayName,
int presentation) {
findConnectionForAction(callId, "setCallerDisplayName")
.setCallerDisplayName(callerDisplayName, presentation);
}
@Override
public void startActivityFromInCall(String callId, PendingIntent intent) {
findConnectionForAction(callId, "startActivityFromInCall")
.startActivityFromInCall(intent);
}
@Override
public IBinder asBinder() {
throw new UnsupportedOperationException();
}
};
RemoteConnectionService(ComponentName componentName, IConnectionService connectionService)
throws RemoteException {
mComponentName = componentName;
mConnectionService = connectionService;
private final ConnectionServiceAdapterServant mServant =
new ConnectionServiceAdapterServant(mServantDelegate);
mConnectionService.addConnectionServiceAdapter(mAdapter);
mConnectionService.asBinder().linkToDeath(this, 0);
private final DeathRecipient mDeathRecipient = new DeathRecipient() {
@Override
public void binderDied() {
for (RemoteConnection c : mConnectionById.values()) {
c.setDestroyed();
}
mConnectionById.clear();
mPendingConnections.clear();
mConnectionService.asBinder().unlinkToDeath(mDeathRecipient, 0);
}
};
private final IConnectionService mConnectionService;
private final Map<String, RemoteConnection> mConnectionById = new HashMap<>();
private final Set<RemoteConnection> mPendingConnections = new HashSet<>();
RemoteConnectionService(IConnectionService connectionService) throws RemoteException {
mConnectionService = connectionService;
mConnectionService.addConnectionServiceAdapter(mServant.getStub());
mConnectionService.asBinder().linkToDeath(mDeathRecipient, 0);
}
@Override
@@ -409,66 +227,37 @@ final class RemoteConnectionService implements DeathRecipient {
return "[RemoteCS - " + mConnectionService.asBinder().toString() + "]";
}
/** ${inheritDoc} */
@Override
public final void binderDied() {
if (mConnection != null) {
destroyConnection();
}
release();
}
final RemoteConnection createRemoteConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
ConnectionRequest request,
boolean isIncoming) {
if (mConnectionId == null) {
String id = UUID.randomUUID().toString();
ConnectionRequest newRequest = new ConnectionRequest(
request.getAccountHandle(),
id,
request.getHandle(),
request.getHandlePresentation(),
request.getExtras(),
request.getVideoState());
mConnection = new RemoteConnection(mConnectionService, request, isIncoming);
try {
mConnectionService.createConnection(
connectionManagerPhoneAccount,
newRequest,
isIncoming);
mConnectionId = id;
} catch (RemoteException e) {
mConnection = RemoteConnection.failure(DisconnectCause.ERROR_UNSPECIFIED,
e.toString());
}
return mConnection;
} else {
return RemoteConnection.failure(DisconnectCause.ERROR_UNSPECIFIED, null);
ConnectionRequest newRequest = new ConnectionRequest(
request.getAccountHandle(),
UUID.randomUUID().toString(),
request.getHandle(),
request.getHandlePresentation(),
request.getExtras(),
request.getVideoState());
try {
mConnectionService.createConnection(
connectionManagerPhoneAccount,
newRequest,
isIncoming);
RemoteConnection connection =
new RemoteConnection(mConnectionService, request);
mPendingConnections.add(connection);
mConnectionById.put(newRequest.getCallId(), connection);
return connection;
} catch (RemoteException e) {
return RemoteConnection.failure(DisconnectCause.ERROR_UNSPECIFIED, e.toString());
}
}
/**
* Releases the resources associated with this Remote connection service. Should be called when
* the remote service is no longer being used.
*/
void release() {
mConnectionService.asBinder().unlinkToDeath(this, 0);
}
private boolean isPendingConnection(String id) {
return TextUtils.equals(mConnectionId, id);
}
private boolean isCurrentConnection(Object obj) {
return obj instanceof String && mConnection != null &&
TextUtils.equals(mConnectionId, (String) obj);
}
private void destroyConnection() {
mConnection.setDestroyed();
mConnection = null;
mConnectionId = null;
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;
}
}