Remote Connection implementation.

API classes for daisy-chaining connection services.

Change-Id: I90991697456377b72ec73d2ef835864cb0b7737a
This commit is contained in:
Santos Cordon
2014-06-17 19:08:45 -07:00
parent 9e58b3c61c
commit 52d8a15e14
12 changed files with 942 additions and 60 deletions

View File

@@ -333,6 +333,7 @@ LOCAL_SRC_FILES += \
telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl \
telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
telephony/java/com/android/internal/telephony/ITelephony.aidl \

View File

@@ -27595,8 +27595,9 @@ package android.telecomm {
method public abstract void unhold(java.lang.String);
}
public final class CallServiceAdapter {
public final class CallServiceAdapter implements android.os.IBinder.DeathRecipient {
method public void addConferenceCall(java.lang.String);
method public void binderDied();
method public void handleFailedOutgoingCall(android.telecomm.ConnectionRequest, int, java.lang.String);
method public void handleSuccessfulOutgoingCall(java.lang.String);
method public void handoffCall(java.lang.String);
@@ -27728,10 +27729,12 @@ package android.telecomm {
public final class ConnectionRequest implements android.os.Parcelable {
ctor public ConnectionRequest(android.net.Uri, android.os.Bundle);
ctor public ConnectionRequest(java.lang.String, android.net.Uri, android.os.Bundle);
ctor public ConnectionRequest(android.telecomm.Subscription, java.lang.String, android.net.Uri, android.os.Bundle);
method public int describeContents();
method public java.lang.String getCallId();
method public android.os.Bundle getExtras();
method public android.net.Uri getHandle();
method public android.telecomm.Subscription getSubscription();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -27741,9 +27744,12 @@ package android.telecomm {
method public final void abort(java.lang.String);
method public final void answer(java.lang.String);
method public final void call(android.telecomm.CallInfo);
method public void createRemoteOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.SimpleResponse<android.telecomm.ConnectionRequest, android.telecomm.RemoteConnection>);
method public final void disconnect(java.lang.String);
method public java.util.Collection<android.telecomm.Connection> getAllConnections();
method public final void hold(java.lang.String);
method public void lookupRemoteSubscriptions(android.net.Uri, android.telecomm.SimpleResponse<android.net.Uri, java.util.List<android.telecomm.Subscription>>);
method public void maybeRespondToSubscriptionLookup();
method public final void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState);
method public void onConnectionAdded(android.telecomm.Connection);
method public void onConnectionRemoved(android.telecomm.Connection);
@@ -27813,11 +27819,46 @@ package android.telecomm {
method protected abstract void updateCall(android.telecomm.InCallCall);
}
public final class RemoteConnection {
method public void addListener(android.telecomm.RemoteConnection.Listener);
method public void answer();
method public void disconnect();
method public int getDisconnectCause();
method public java.lang.String getDisconnectMessage();
method public void hold();
method public void playDtmf(char);
method public void postDialContinue(boolean);
method public void reject();
method public void removeListener(android.telecomm.RemoteConnection.Listener);
method public void stopDtmf();
method public void unhold();
}
public static abstract interface RemoteConnection.Listener {
method public abstract void onAudioStateChanged(android.telecomm.RemoteConnection, android.telecomm.CallAudioState);
method public abstract void onDestroyed(android.telecomm.RemoteConnection);
method public abstract void onDisconnected(android.telecomm.RemoteConnection, int, java.lang.String);
method public abstract void onPostDialWait(android.telecomm.RemoteConnection, java.lang.String);
method public abstract void onRequestingRingback(android.telecomm.RemoteConnection, boolean);
method public abstract void onStateChanged(android.telecomm.RemoteConnection, int);
}
public class RemoteConnectionService implements android.os.IBinder.DeathRecipient {
method public void binderDied();
method public void createOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.SimpleResponse<android.telecomm.ConnectionRequest, android.telecomm.RemoteConnection>);
method public java.util.List<android.telecomm.Subscription> lookupSubscriptions(android.net.Uri);
}
public abstract interface Response {
method public abstract void onError(IN, int, java.lang.String);
method public abstract void onResult(IN, OUT...);
}
public abstract interface SimpleResponse {
method public abstract void onError(IN);
method public abstract void onResult(IN, OUT);
}
public class Subscription implements android.os.Parcelable {
ctor public Subscription(android.content.ComponentName, java.lang.String, android.net.Uri, int, int, int, boolean, boolean);
method public int describeContents();
@@ -38353,6 +38394,22 @@ package android.widget {
}
package com.android.internal.telecomm {
public abstract interface RemoteServiceCallback implements android.os.IInterface {
method public abstract void onError() throws android.os.RemoteException;
method public abstract void onResult(java.util.List<android.content.ComponentName>, java.util.List<android.os.IBinder>) throws android.os.RemoteException;
}
public static abstract class RemoteServiceCallback.Stub extends android.os.Binder implements com.android.internal.telecomm.RemoteServiceCallback {
ctor public RemoteServiceCallback.Stub();
method public android.os.IBinder asBinder();
method public static com.android.internal.telecomm.RemoteServiceCallback asInterface(android.os.IBinder);
method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
}
}
package com.android.internal.util {
public abstract interface Predicate {

View File

@@ -27,8 +27,6 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.telecomm.ICallService;
import com.android.internal.telecomm.ICallServiceAdapter;
import java.util.List;
/**
* Base implementation of CallService which can be used to provide calls for the system
* in-call UI. CallService is a one-way service from the framework's CallsManager to any app
@@ -72,7 +70,7 @@ public abstract class CallService extends Service {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_CALL_SERVICE_ADAPTER:
mAdapter = new CallServiceAdapter((ICallServiceAdapter) msg.obj);
mAdapter.addAdapter((ICallServiceAdapter) msg.obj);
onAdapterAttached(mAdapter);
break;
case MSG_CALL:
@@ -259,7 +257,7 @@ public abstract class CallService extends Service {
*/
private final CallServiceBinder mBinder = new CallServiceBinder();
private CallServiceAdapter mAdapter = null;
private CallServiceAdapter mAdapter = new CallServiceAdapter();
/** {@inheritDoc} */
@Override

View File

@@ -16,38 +16,86 @@
package android.telecomm;
import android.content.ComponentName;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import com.android.internal.telecomm.ICallService;
import com.android.internal.telecomm.ICallServiceAdapter;
import com.android.internal.telecomm.RemoteServiceCallback;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Provides methods for ICallService implementations to interact with the system phone app.
* TODO(santoscordon): Need final public-facing comments in this file.
* TODO(santoscordon): Rename this to CallServiceAdapterDemultiplexer (or something).
*/
public final class CallServiceAdapter {
private final ICallServiceAdapter mAdapter;
public final class CallServiceAdapter implements DeathRecipient {
private final Set<ICallServiceAdapter> mAdapters = new HashSet<>();
/**
* {@hide}
* @hide
*/
public CallServiceAdapter(ICallServiceAdapter adapter) {
mAdapter = adapter;
public CallServiceAdapter() {
}
/**
* @hide
*/
public void addAdapter(ICallServiceAdapter adapter) {
if (mAdapters.add(adapter)) {
try {
adapter.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
mAdapters.remove(adapter);
}
}
}
/**
* @hide
*/
public void removeAdapter(ICallServiceAdapter adapter) {
if (mAdapters.remove(adapter)) {
adapter.asBinder().unlinkToDeath(this, 0);
}
}
/** ${inheritDoc} */
@Override
public void binderDied() {
ICallServiceAdapter adapterToRemove = null;
for (ICallServiceAdapter adapter : mAdapters) {
if (!adapter.asBinder().isBinderAlive()) {
adapterToRemove = adapter;
break;
}
}
if (adapterToRemove != null) {
removeAdapter(adapterToRemove);
}
}
/**
* Provides Telecomm with the details of an incoming call. An invocation of this method must
* follow {@link CallService#setIncomingCallId} and use the call ID specified therein. Upon
* the invocation of this method, Telecomm will bring up the incoming-call interface where the
* user can elect to answer or reject a call.
* follow {@link CallService#setIncomingCallId} and use the call ID specified therein. Upon the
* invocation of this method, Telecomm will bring up the incoming-call interface where the user
* can elect to answer or reject a call.
*
* @param callInfo The details of the relevant call.
*/
public void notifyIncomingCall(CallInfo callInfo) {
try {
mAdapter.notifyIncomingCall(callInfo);
} catch (RemoteException e) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.notifyIncomingCall(callInfo);
} catch (RemoteException e) {
}
}
}
@@ -59,9 +107,11 @@ public final class CallServiceAdapter {
* @param callId The ID of the outgoing call.
*/
public void handleSuccessfulOutgoingCall(String callId) {
try {
mAdapter.handleSuccessfulOutgoingCall(callId);
} catch (RemoteException e) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.handleSuccessfulOutgoingCall(callId);
} catch (RemoteException e) {
}
}
}
@@ -76,9 +126,11 @@ public final class CallServiceAdapter {
ConnectionRequest request,
int errorCode,
String errorMsg) {
try {
mAdapter.handleFailedOutgoingCall(request, errorCode, errorMsg);
} catch (RemoteException e) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.handleFailedOutgoingCall(request, errorCode, errorMsg);
} catch (RemoteException e) {
}
}
}
@@ -89,9 +141,11 @@ public final class CallServiceAdapter {
* @param callId The unique ID of the call whose state is changing to active.
*/
public void setActive(String callId) {
try {
mAdapter.setActive(callId);
} catch (RemoteException e) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.setActive(callId);
} catch (RemoteException e) {
}
}
}
@@ -101,9 +155,11 @@ public final class CallServiceAdapter {
* @param callId The unique ID of the call whose state is changing to ringing.
*/
public void setRinging(String callId) {
try {
mAdapter.setRinging(callId);
} catch (RemoteException e) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.setRinging(callId);
} catch (RemoteException e) {
}
}
}
@@ -113,9 +169,11 @@ public final class CallServiceAdapter {
* @param callId The unique ID of the call whose state is changing to dialing.
*/
public void setDialing(String callId) {
try {
mAdapter.setDialing(callId);
} catch (RemoteException e) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.setDialing(callId);
} catch (RemoteException e) {
}
}
}
@@ -124,13 +182,15 @@ public final class CallServiceAdapter {
*
* @param callId The unique ID of the call whose state is changing to disconnected.
* @param disconnectCause The reason for the disconnection, any of
* {@link android.telephony.DisconnectCause}.
* {@link android.telephony.DisconnectCause}.
* @param disconnectMessage Optional call-service-provided message about the disconnect.
*/
public void setDisconnected(String callId, int disconnectCause, String disconnectMessage) {
try {
mAdapter.setDisconnected(callId, disconnectCause, disconnectMessage);
} catch (RemoteException e) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.setDisconnected(callId, disconnectCause, disconnectMessage);
} catch (RemoteException e) {
}
}
}
@@ -140,9 +200,11 @@ public final class CallServiceAdapter {
* @param callId - The unique ID of the call whose state is changing to be on hold.
*/
public void setOnHold(String callId) {
try {
mAdapter.setOnHold(callId);
} catch (RemoteException e) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.setOnHold(callId);
} catch (RemoteException e) {
}
}
}
@@ -153,9 +215,11 @@ public final class CallServiceAdapter {
* @param ringback Whether Telecomm should start playing a ringback tone.
*/
public void setRequestingRingback(String callId, boolean ringback) {
try {
mAdapter.setRequestingRingback(callId, ringback);
} catch (RemoteException e) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.setRequestingRingback(callId, ringback);
} catch (RemoteException e) {
}
}
}
@@ -167,9 +231,11 @@ public final class CallServiceAdapter {
* @hide
*/
public void setCanConference(String callId, boolean canConference) {
try {
mAdapter.setCanConference(callId, canConference);
} catch (RemoteException ignored) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.setCanConference(callId, canConference);
} catch (RemoteException ignored) {
}
}
}
@@ -179,13 +245,15 @@ public final class CallServiceAdapter {
*
* @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.
* conferenced.
* @hide
*/
public void setIsConferenced(String callId, String conferenceCallId) {
try {
mAdapter.setIsConferenced(callId, conferenceCallId);
} catch (RemoteException ignored) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.setIsConferenced(callId, conferenceCallId);
} catch (RemoteException ignored) {
}
}
}
@@ -197,16 +265,20 @@ public final class CallServiceAdapter {
* @hide
*/
public void removeCall(String callId) {
try {
mAdapter.removeCall(callId);
} catch (RemoteException ignored) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.removeCall(callId);
} catch (RemoteException ignored) {
}
}
}
public void onPostDialWait(String callId, String remaining) {
try {
mAdapter.onPostDialWait(callId, remaining);
} catch (RemoteException ignored) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.onPostDialWait(callId, remaining);
} catch (RemoteException ignored) {
}
}
}
@@ -216,9 +288,11 @@ public final class CallServiceAdapter {
* @param callId The identifier of the call to handoff.
*/
public void handoffCall(String callId) {
try {
mAdapter.handoffCall(callId);
} catch (RemoteException e) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.handoffCall(callId);
} catch (RemoteException e) {
}
}
}
@@ -228,9 +302,26 @@ public final class CallServiceAdapter {
* @param callId The unique ID of the conference call.
*/
public void addConferenceCall(String callId) {
try {
mAdapter.addConferenceCall(callId, null);
} catch (RemoteException ignored) {
for (ICallServiceAdapter adapter : mAdapters) {
try {
adapter.addConferenceCall(callId, null);
} catch (RemoteException ignored) {
}
}
}
/**
* Retrieves a list of remote connection services usable to place calls.
* @hide
*/
public 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");
}
}
}
}

View File

@@ -33,17 +33,28 @@ public final class ConnectionRequest implements Parcelable {
private final String mCallId;
private final Uri mHandle;
private final Bundle mExtras;
private final Subscription mSubscription;
public ConnectionRequest(Uri handle, Bundle extras) {
this(null, handle, extras);
}
public ConnectionRequest(String callId, Uri handle, Bundle extras) {
this(null, callId, handle, extras);
}
public ConnectionRequest(Subscription subscription, String callId, Uri handle, Bundle extras) {
mCallId = callId;
mHandle = handle;
mExtras = extras;
mSubscription = subscription;
}
/**
* The subscription which should be used to place the call.
*/
public Subscription getSubscription() { return mSubscription; }
/**
* An identifier for this call.
*/

View File

@@ -16,12 +16,21 @@
package android.telecomm;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.telephony.DisconnectCause;
import com.android.internal.telecomm.ICallService;
import com.android.internal.telecomm.RemoteServiceCallback;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@@ -36,6 +45,12 @@ public abstract class ConnectionService extends CallService {
// Mappings from Connections to IDs as understood by the current CallService implementation
private final Map<String, Connection> mConnectionById = new HashMap<>();
private final Map<Connection, String> mIdByConnection = new HashMap<>();
private final RemoteConnectionManager mRemoteConnectionManager = new RemoteConnectionManager();
private final Handler mHandler = new Handler(Looper.getMainLooper());
private SimpleResponse<Uri, List<Subscription>> mSubscriptionLookupResponse;
private Uri mSubscriptionLookupHandle;
private boolean mAreSubscriptionsInitialized = false;
private final Connection.Listener mConnectionListener = new Connection.Listener() {
@Override
@@ -313,6 +328,71 @@ public abstract class ConnectionService extends CallService {
getAdapter().onPostDialWait(mIdByConnection.get(conn), remaining);
}
/**
* @hide
*/
@Override
protected void onAdapterAttached(CallServiceAdapter adapter) {
if (mAreSubscriptionsInitialized) {
// No need to query again if we already did it.
return;
}
getAdapter().queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
@Override
public void onResult(
final List<ComponentName> componentNames,
final List<IBinder> callServices) {
mHandler.post(new Runnable() {
@Override public void run() {
for (int i = 0; i < componentNames.size() && i < callServices.size(); i++) {
mRemoteConnectionManager.addConnectionService(
componentNames.get(i),
ICallService.Stub.asInterface(callServices.get(i)));
}
mAreSubscriptionsInitialized = true;
Log.d(this, "remote call services found: " + callServices);
maybeRespondToSubscriptionLookup();
}
});
}
@Override
public void onError() {
mHandler.post(new Runnable() {
@Override public void run() {
mAreSubscriptionsInitialized = true;
maybeRespondToSubscriptionLookup();
}
});
}
});
}
public void lookupRemoteSubscriptions(
Uri handle, SimpleResponse<Uri, List<Subscription>> response) {
mSubscriptionLookupResponse = response;
mSubscriptionLookupHandle = handle;
maybeRespondToSubscriptionLookup();
}
public void maybeRespondToSubscriptionLookup() {
if (mAreSubscriptionsInitialized && mSubscriptionLookupResponse != null) {
mSubscriptionLookupResponse.onResult(
mSubscriptionLookupHandle,
mRemoteConnectionManager.getSubscriptions(mSubscriptionLookupHandle));
mSubscriptionLookupHandle = null;
mSubscriptionLookupResponse = null;
}
}
public void createRemoteOutgoingConnection(
ConnectionRequest request,
SimpleResponse<ConnectionRequest, RemoteConnection> response) {
mRemoteConnectionManager.createOutgoingConnection(request, response);
}
/**
* Returns all connections currently associated with this connection service.
*/

View File

@@ -0,0 +1,227 @@
/*
* 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.telecomm;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.telephony.DisconnectCause;
import com.android.internal.telecomm.ICallService;
import java.util.HashSet;
import java.util.Set;
/**
* RemoteConnection object used by RemoteConnectionService.
*/
public final class RemoteConnection {
public interface Listener {
void onStateChanged(RemoteConnection connection, int state);
void onAudioStateChanged(RemoteConnection connection, CallAudioState state);
void onDisconnected(RemoteConnection connection, int cause, String message);
void onRequestingRingback(RemoteConnection connection, boolean ringback);
void onPostDialWait(RemoteConnection connection, String remainingDigits);
void onDestroyed(RemoteConnection connection);
}
private final ICallService mCallService;
private final String mConnectionId;
private final Set<Listener> mListeners = new HashSet<>();
private int mState;
private int mDisconnectCause = DisconnectCause.NOT_VALID;
private String mDisconnectMessage;
private boolean mRequestingRingback;
private boolean mConnected;
/**
* @hide
*/
RemoteConnection(ICallService callService, String connectionId) {
mCallService = callService;
mConnectionId = connectionId;
mConnected = true;
}
public void addListener(Listener listener) {
mListeners.add(listener);
}
public void removeListener(Listener listener) {
mListeners.remove(listener);
}
public int getDisconnectCause() {
return mDisconnectCause;
}
public String getDisconnectMessage() {
return mDisconnectMessage;
}
public void answer() {
try {
if (mConnected) {
mCallService.answer(mConnectionId);
}
} catch (RemoteException ignored) {
}
}
public void reject() {
try {
if (mConnected) {
mCallService.reject(mConnectionId);
}
} catch (RemoteException ignored) {
}
}
public void hold() {
try {
if (mConnected) {
mCallService.hold(mConnectionId);
}
} catch (RemoteException ignored) {
}
}
public void unhold() {
try {
if (mConnected) {
mCallService.unhold(mConnectionId);
}
} catch (RemoteException ignored) {
}
}
public void disconnect() {
try {
if (mConnected) {
mCallService.disconnect(mConnectionId);
}
} catch (RemoteException ignored) {
}
}
public void playDtmf(char digit) {
try {
if (mConnected) {
mCallService.playDtmfTone(mConnectionId, digit);
}
} catch (RemoteException ignored) {
}
}
public void stopDtmf() {
try {
if (mConnected) {
mCallService.stopDtmfTone(mConnectionId);
}
} catch (RemoteException ignored) {
}
}
public void postDialContinue(boolean proceed) {
try {
if (mConnected) {
mCallService.onPostDialContinue(mConnectionId, proceed);
}
} catch (RemoteException ignored) {
}
}
/**
* @hide
*/
void setState(int state) {
if (mState != state) {
mState = state;
for (Listener l: mListeners) {
l.onStateChanged(this, state);
}
}
}
/**
* @hide
*/
void setAudioState(CallAudioState state) {
for (Listener l: mListeners) {
l.onAudioStateChanged(this, state);
}
}
/**
* @hide
*/
void setDisconnected(int cause, String message) {
if (mState != Connection.State.DISCONNECTED) {
mState = Connection.State.DISCONNECTED;
mDisconnectCause = cause;
mDisconnectMessage = message;
for (Listener l : mListeners) {
l.onDisconnected(this, cause, message);
}
}
}
/**
* @hide
*/
void setRequestingRingback(boolean ringback) {
if (mRequestingRingback != ringback) {
mRequestingRingback = ringback;
for (Listener l : mListeners) {
l.onRequestingRingback(this, ringback);
}
}
}
/**
* @hide
*/
void setDestroyed() {
if (!mListeners.isEmpty()) {
// Make sure that the listeners are notified that the call is destroyed first.
if (mState != Connection.State.DISCONNECTED) {
setDisconnected(DisconnectCause.ERROR_UNSPECIFIED, "Connection destroyed.");
}
Set<Listener> listeners = new HashSet<Listener>(mListeners);
mListeners.clear();
for (Listener l : listeners) {
l.onDestroyed(this);
}
mConnected = false;
}
}
/**
* @hide
*/
void setPostDialWait(String remainingDigits) {
for (Listener l : mListeners) {
l.onPostDialWait(this, remainingDigits);
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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 android.content.ComponentName;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
import com.android.internal.telecomm.ICallService;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @hide
*/
public class RemoteConnectionManager {
private Map<ComponentName, RemoteConnectionService> mRemoteConnectionServices = new HashMap<>();
void addConnectionService(ComponentName componentName, ICallService callService) {
if (!mRemoteConnectionServices.containsKey(componentName)) {
try {
RemoteConnectionService remoteConnectionService =
new RemoteConnectionService(componentName, callService);
mRemoteConnectionServices.put(componentName, remoteConnectionService);
} catch (RemoteException ignored) {
}
}
}
List<Subscription> getSubscriptions(Uri handle) {
List<Subscription> subscriptions = new LinkedList<>();
Log.d(this, "Getting subscriptions: " + mRemoteConnectionServices.keySet());
for (RemoteConnectionService remoteService : mRemoteConnectionServices.values()) {
// TODO(santoscordon): Eventually this will be async.
subscriptions.addAll(remoteService.lookupSubscriptions(handle));
}
return subscriptions;
}
public void createOutgoingConnection(
ConnectionRequest request,
final SimpleResponse<ConnectionRequest, RemoteConnection> response) {
Subscription subscription = request.getSubscription();
if (subscription == null) {
throw new IllegalArgumentException("subscription must be specified.");
}
ComponentName componentName = request.getSubscription().getComponentName();
if (!mRemoteConnectionServices.containsKey(componentName)) {
throw new UnsupportedOperationException("subscription not supported: " + componentName);
} else {
RemoteConnectionService remoteService = mRemoteConnectionServices.get(componentName);
remoteService.createOutgoingConnection(request, response);
}
}
}

View File

@@ -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
R* limitations under the License.
*/
package android.telecomm;
import android.content.ComponentName;
import android.net.Uri;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.telephony.DisconnectCause;
import android.text.TextUtils;
import com.android.internal.telecomm.ICallService;
import com.android.internal.telecomm.ICallServiceAdapter;
import com.android.internal.telecomm.RemoteServiceCallback;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
/**
* Remote connection service which other connection services can use to place calls on their behalf.
*/
public class RemoteConnectionService implements DeathRecipient {
private final ICallService mCallService;
private final ComponentName mComponentName;
private String mConnectionId;
private ConnectionRequest mPendingRequest;
private SimpleResponse<ConnectionRequest, RemoteConnection> mPendingResponse;
// Remote connection services only support a single connection.
private RemoteConnection mConnection;
private final ICallServiceAdapter mAdapter = new ICallServiceAdapter.Stub() {
/** ${inheritDoc} */
@Override
public void notifyIncomingCall(CallInfo callInfo) {
Log.w(this, "notifyIncomingCall not implemented in Remote connection");
}
/** ${inheritDoc} */
@Override
public void handleSuccessfulOutgoingCall(String connectionId) {
if (isPendingConnection(connectionId)) {
mConnection = new RemoteConnection(mCallService, connectionId);
mPendingResponse.onResult(mPendingRequest, mConnection);
clearPendingInformation();
}
}
/** ${inheritDoc} */
@Override
public void handleFailedOutgoingCall(
ConnectionRequest request, int errorCode, String errorMessage) {
if (isPendingConnection(request.getCallId())) {
// Use mPendingRequest instead of request so that we use the same object that was
// passed in to us.
mPendingResponse.onError(request);
mConnectionId = null;
clearPendingInformation();
}
}
/** ${inheritDoc} */
@Override
public void setActive(String connectionId) {
if (isCurrentConnection(connectionId)) {
mConnection.setState(Connection.State.ACTIVE);
}
}
/** ${inheritDoc} */
@Override
public void setRinging(String connectionId) {
if (isCurrentConnection(connectionId)) {
mConnection.setState(Connection.State.RINGING);
}
}
/** ${inheritDoc} */
@Override
public void setDialing(String connectionId) {
if (isCurrentConnection(connectionId)) {
mConnection.setState(Connection.State.DIALING);
}
}
/** ${inheritDoc} */
@Override
public void setDisconnected(
String connectionId, int disconnectCause, String disconnectMessage) {
if (isCurrentConnection(connectionId)) {
mConnection.setDisconnected(disconnectCause, disconnectMessage);
}
}
/** ${inheritDoc} */
@Override
public void setOnHold(String connectionId) {
if (isCurrentConnection(connectionId)) {
mConnection.setState(Connection.State.HOLDING);
}
}
/** ${inheritDoc} */
@Override
public void setRequestingRingback(String connectionId, boolean isRequestingRingback) {
if (isCurrentConnection(connectionId)) {
mConnection.setRequestingRingback(isRequestingRingback);
}
}
/** ${inheritDoc} */
@Override
public void setCanConference(String connectionId, boolean canConference) {
// not supported for remote connections.
}
/** ${inheritDoc} */
@Override
public void setIsConferenced(String connectionId, String conferenceConnectionId) {
// not supported for remote connections.
}
/** ${inheritDoc} */
@Override
public void addConferenceCall(String connectionId, CallInfo callInfo) {
// not supported for remote connections.
}
/** ${inheritDoc} */
@Override
public void removeCall(String connectionId) {
if (isCurrentConnection(connectionId)) {
destroyConnection();
}
}
/** ${inheritDoc} */
@Override
public void onPostDialWait(String connectionId, String remainingDigits) {
if (isCurrentConnection(connectionId)) {
mConnection.setPostDialWait(remainingDigits);
}
}
/** ${inheritDoc} */
@Override
public void handoffCall(String connectionId) {
// unnecessary.
}
/** ${inheritDoc} */
@Override
public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
try {
// Not supported from remote connection service.
callback.onError();
} catch (RemoteException e) {
}
}
};
RemoteConnectionService(ComponentName componentName, ICallService callService)
throws RemoteException {
mComponentName = componentName;
mCallService = callService;
// TODO(santoscordon): Rename from setCallServiceAdapter to addCallServiceAdapter.
mCallService.setCallServiceAdapter(mAdapter);
mCallService.asBinder().linkToDeath(this, 0);
}
@Override
public String toString() {
return "[RemoteCS - " + mCallService.asBinder().toString() + "]";
}
/** ${inheritDoc} */
@Override
public void binderDied() {
if (mConnection != null) {
destroyConnection();
}
release();
}
/**
* Places an outgoing call.
*/
public void createOutgoingConnection(
ConnectionRequest request,
SimpleResponse<ConnectionRequest, RemoteConnection> response) {
if (mConnectionId == null) {
String id = UUID.randomUUID().toString();
CallInfo callInfo = new CallInfo(id, CallState.NEW, request.getHandle());
try {
mCallService.call(callInfo);
mConnectionId = id;
mPendingResponse = response;
mPendingRequest = request;
} catch (RemoteException e) {
response.onError(request);
}
} else {
response.onError(request);
}
}
// TODO(santoscordon): Handle incoming connections
// public void handleIncomingConnection() {}
public List<Subscription> lookupSubscriptions(Uri handle) {
// TODO(santoscordon): Update this so that is actually calls into the RemoteConnection
// each time.
List<Subscription> subscriptions = new LinkedList<>();
subscriptions.add(new Subscription(
mComponentName,
null /* id */,
null /* handle */,
0 /* labelResId */,
0 /* shortDescriptionResId */,
0 /* iconResId */,
true /* isEnabled */,
false /* isSystemDefault */));
return subscriptions;
}
/**
* Releases the resources associated with this Remote connection service. Should be called when
* the remote service is no longer being used.
*/
void release() {
mCallService.asBinder().unlinkToDeath(this, 0);
}
private boolean isPendingConnection(String id) {
return TextUtils.equals(mConnectionId, id) && mPendingResponse != null;
}
private boolean isCurrentConnection(String id) {
return mConnection != null && TextUtils.equals(mConnectionId, id);
}
private void clearPendingInformation() {
mPendingRequest = null;
mPendingResponse = null;
}
private void destroyConnection() {
mConnection.setDestroyed();
mConnection = null;
mConnectionId = null;
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.telecomm;
/**
* Used to inform a client of asynchronously returned results.
*/
public interface SimpleResponse<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.
*/
void onError(IN request);
}

View File

@@ -19,6 +19,8 @@ package com.android.internal.telecomm;
import android.telecomm.CallInfo;
import android.telecomm.ConnectionRequest;
import com.android.internal.telecomm.RemoteServiceCallback;
/**
* Internal remote callback interface for call services.
*
@@ -56,4 +58,6 @@ oneway interface ICallServiceAdapter {
void onPostDialWait(String callId, String remaining);
void handoffCall(String callId);
void queryRemoteConnectionServices(RemoteServiceCallback callback);
}

View File

@@ -0,0 +1,27 @@
/*
* 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.telecomm;
import android.content.ComponentName;
/**
* Simple response callback object.
*/
oneway interface RemoteServiceCallback {
void onError();
void onResult(in List<ComponentName> components, in List<IBinder> callServices);
}