| /* |
| * Copyright 2014, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.telecomm; |
| |
| import android.app.PendingIntent; |
| import android.content.ComponentName; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Message; |
| import android.os.RemoteException; |
| import android.telecomm.CallAudioState; |
| import android.telecomm.ConnectionRequest; |
| import android.telecomm.ConnectionService; |
| import android.telecomm.GatewayInfo; |
| import android.telecomm.ParcelableConnection; |
| import android.telecomm.StatusHints; |
| import android.telephony.DisconnectCause; |
| |
| import com.android.internal.os.SomeArgs; |
| import com.android.internal.telecomm.ICallVideoProvider; |
| import com.android.internal.telecomm.IConnectionService; |
| import com.android.internal.telecomm.IConnectionServiceAdapter; |
| import com.android.internal.telecomm.RemoteServiceCallback; |
| import com.google.common.base.Preconditions; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps |
| * track of when the object can safely be unbound. Other classes should not use |
| * {@link IConnectionService} directly and instead should use this class to invoke methods of |
| * {@link IConnectionService}. |
| */ |
| final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> { |
| 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_CALL_VIDEO_PROVIDER = 16; |
| private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 17; |
| private static final int MSG_SET_STATUS_HINTS = 18; |
| private static final int MSG_SET_HANDLE = 19; |
| private static final int MSG_SET_CALLER_DISPLAY_NAME = 20; |
| private static final int MSG_SET_VIDEO_STATE = 21; |
| private static final int MSG_START_ACTIVITY_FROM_IN_CALL = 22; |
| |
| private final Handler mHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| Call call; |
| switch (msg.what) { |
| case MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| try { |
| ConnectionRequest request = (ConnectionRequest) args.arg1; |
| if (mPendingResponses.containsKey(request.getCallId())) { |
| ParcelableConnection connection = (ParcelableConnection) args.arg2; |
| mPendingResponses.remove(request.getCallId()). |
| handleCreateConnectionSuccessful(request, connection); |
| } |
| } finally { |
| args.recycle(); |
| } |
| break; |
| } |
| case MSG_HANDLE_CREATE_CONNECTION_FAILED: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| try { |
| ConnectionRequest request = (ConnectionRequest) args.arg1; |
| int statusCode = args.argi1; |
| String statusMsg = (String) args.arg2; |
| removeCall( |
| mCallIdMapper.getCall(request.getCallId()), |
| statusCode, |
| statusMsg); |
| } finally { |
| args.recycle(); |
| } |
| break; |
| } |
| case MSG_HANDLE_CREATE_CONNECTION_CANCELLED: { |
| ConnectionRequest request = (ConnectionRequest) msg.obj; |
| if (mPendingResponses.containsKey(request.getCallId())) { |
| mPendingResponses.remove( |
| request.getCallId()).handleCreateConnectionCancelled(); |
| } else { |
| //Log.w(this, "handleCreateConnectionCancelled, unknown call: %s", callId); |
| } |
| break; |
| } |
| case MSG_SET_ACTIVE: |
| call = mCallIdMapper.getCall(msg.obj); |
| if (call != null) { |
| mCallsManager.markCallAsActive(call); |
| } else { |
| //Log.w(this, "setActive, unknown call id: %s", msg.obj); |
| } |
| break; |
| case MSG_SET_RINGING: |
| call = mCallIdMapper.getCall(msg.obj); |
| if (call != null) { |
| mCallsManager.markCallAsRinging(call); |
| } else { |
| //Log.w(this, "setRinging, unknown call id: %s", msg.obj); |
| } |
| break; |
| case MSG_SET_DIALING: |
| call = mCallIdMapper.getCall(msg.obj); |
| if (call != null) { |
| mCallsManager.markCallAsDialing(call); |
| } else { |
| //Log.w(this, "setDialing, unknown call id: %s", msg.obj); |
| } |
| break; |
| case MSG_SET_DISCONNECTED: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| try { |
| call = mCallIdMapper.getCall(args.arg1); |
| String disconnectMessage = (String) args.arg2; |
| int disconnectCause = args.argi1; |
| if (call != null) { |
| mCallsManager.markCallAsDisconnected(call, disconnectCause, |
| disconnectMessage); |
| } else { |
| //Log.w(this, "setDisconnected, unknown call id: %s", args.arg1); |
| } |
| } finally { |
| args.recycle(); |
| } |
| break; |
| } |
| case MSG_SET_ON_HOLD: |
| call = mCallIdMapper.getCall(msg.obj); |
| if (call != null) { |
| mCallsManager.markCallAsOnHold(call); |
| } else { |
| //Log.w(this, "setOnHold, unknown call id: %s", msg.obj); |
| } |
| break; |
| case MSG_SET_REQUESTING_RINGBACK: { |
| call = mCallIdMapper.getCall(msg.obj); |
| if (call != null) { |
| call.setRequestingRingback(msg.arg1 == 1); |
| } else { |
| //Log.w(this, "setRingback, unknown call id: %s", args.arg1); |
| } |
| break; |
| } |
| case MSG_SET_CALL_CAPABILITIES: { |
| call = mCallIdMapper.getCall(msg.obj); |
| if (call != null) { |
| call.setCallCapabilities(msg.arg1); |
| } else { |
| //Log.w(ConnectionServiceWrapper.this, |
| // "setCallCapabilities, unknown call id: %s", msg.obj); |
| } |
| break; |
| } |
| case MSG_SET_IS_CONFERENCED: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| try { |
| Call childCall = mCallIdMapper.getCall(args.arg1); |
| if (childCall != null) { |
| String conferenceCallId = (String) args.arg2; |
| if (conferenceCallId == null) { |
| childCall.setParentCall(null); |
| } else { |
| Call conferenceCall = mCallIdMapper.getCall(conferenceCallId); |
| if (conferenceCall != null && |
| !mPendingConferenceCalls.contains(conferenceCall)) { |
| childCall.setParentCall(conferenceCall); |
| } else { |
| //Log.w(this, "setIsConferenced, unknown conference id %s", |
| // conferenceCallId); |
| } |
| } |
| } else { |
| //Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1); |
| } |
| } finally { |
| args.recycle(); |
| } |
| break; |
| } |
| case MSG_ADD_CONFERENCE_CALL: { |
| Call conferenceCall = mCallIdMapper.getCall(msg.obj); |
| if (mPendingConferenceCalls.remove(conferenceCall)) { |
| Log.v(this, "confirming conf call %s", conferenceCall); |
| conferenceCall.confirmConference(); |
| } else { |
| //Log.w(this, "addConference, unknown call id: %s", callId); |
| } |
| break; |
| } |
| case MSG_REMOVE_CALL: |
| break; |
| case MSG_ON_POST_DIAL_WAIT: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| try { |
| call = mCallIdMapper.getCall(args.arg1); |
| if (call != null) { |
| String remaining = (String) args.arg2; |
| call.onPostDialWait(remaining); |
| } else { |
| //Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1); |
| } |
| } finally { |
| args.recycle(); |
| } |
| break; |
| } |
| case MSG_QUERY_REMOTE_CALL_SERVICES: { |
| queryRemoteConnectionServices((RemoteServiceCallback) msg.obj); |
| break; |
| } |
| case MSG_SET_CALL_VIDEO_PROVIDER: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| try { |
| call = mCallIdMapper.getCall(args.arg1); |
| ICallVideoProvider callVideoProvider = (ICallVideoProvider) args.arg2; |
| if (call != null) { |
| call.setCallVideoProvider(callVideoProvider); |
| } |
| } finally { |
| args.recycle(); |
| } |
| break; |
| } |
| case MSG_SET_AUDIO_MODE_IS_VOIP: { |
| call = mCallIdMapper.getCall(msg.obj); |
| if (call != null) { |
| call.setAudioModeIsVoip(msg.arg1 == 1); |
| } |
| break; |
| } |
| case MSG_SET_STATUS_HINTS: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| try { |
| call = mCallIdMapper.getCall(args.arg1); |
| StatusHints statusHints = (StatusHints) args.arg2; |
| if (call != null) { |
| call.setStatusHints(statusHints); |
| } |
| } finally { |
| args.recycle(); |
| } |
| break; |
| } |
| case MSG_SET_HANDLE: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| try { |
| call = mCallIdMapper.getCall(args.arg1); |
| if (call != null) { |
| call.setHandle((Uri) args.arg2, args.argi1); |
| } |
| } finally { |
| args.recycle(); |
| } |
| break; |
| } |
| case MSG_SET_CALLER_DISPLAY_NAME: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| try { |
| call = mCallIdMapper.getCall(args.arg1); |
| if (call != null) { |
| call.setCallerDisplayName((String) args.arg2, args.argi1); |
| } |
| } finally { |
| args.recycle(); |
| } |
| break; |
| } |
| case MSG_SET_VIDEO_STATE: { |
| call = mCallIdMapper.getCall(msg.obj); |
| if (call != null) { |
| call.setVideoState(msg.arg1); |
| } |
| break; |
| } |
| case MSG_START_ACTIVITY_FROM_IN_CALL: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| try { |
| call = mCallIdMapper.getCall(args.arg1); |
| if (call != null) { |
| call.startActivityFromInCall((PendingIntent) args.arg2); |
| } |
| } finally { |
| args.recycle(); |
| } |
| break; |
| } |
| } |
| } |
| }; |
| |
| private final class Adapter extends IConnectionServiceAdapter.Stub { |
| @Override |
| public void handleCreateConnectionSuccessful( |
| ConnectionRequest request, ParcelableConnection connection) { |
| |
| logIncoming("handleCreateConnectionSuccessful %s", request); |
| mCallIdMapper.checkValidCallId(request.getCallId()); |
| 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 errorMsg) { |
| logIncoming("handleCreateConnectionFailed %s %d %s", request, errorCode, errorMsg); |
| mCallIdMapper.checkValidCallId(request.getCallId()); |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = request; |
| args.argi1 = errorCode; |
| args.arg2 = errorMsg; |
| mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_FAILED, args).sendToTarget(); |
| } |
| |
| @Override |
| public void handleCreateConnectionCancelled(ConnectionRequest request) { |
| logIncoming("handleCreateConnectionCancelled %s", request); |
| mCallIdMapper.checkValidCallId(request.getCallId()); |
| mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_CANCELLED, request).sendToTarget(); |
| } |
| |
| @Override |
| public void setActive(String callId) { |
| logIncoming("setActive %s", callId); |
| mCallIdMapper.checkValidCallId(callId); |
| mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget(); |
| } |
| |
| @Override |
| public void setRinging(String callId) { |
| logIncoming("setRinging %s", callId); |
| mCallIdMapper.checkValidCallId(callId); |
| mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget(); |
| } |
| |
| @Override |
| public void setCallVideoProvider(String callId, ICallVideoProvider callVideoProvider) { |
| logIncoming("setCallVideoProvider %s", callId); |
| mCallIdMapper.checkValidCallId(callId); |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = callId; |
| args.arg2 = callVideoProvider; |
| mHandler.obtainMessage(MSG_SET_CALL_VIDEO_PROVIDER, args).sendToTarget(); |
| } |
| |
| @Override |
| public void setDialing(String callId) { |
| logIncoming("setDialing %s", callId); |
| mCallIdMapper.checkValidCallId(callId); |
| mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget(); |
| } |
| |
| @Override |
| public void setDisconnected( |
| String callId, int disconnectCause, String disconnectMessage) { |
| logIncoming("setDisconnected %s %d %s", callId, disconnectCause, disconnectMessage); |
| mCallIdMapper.checkValidCallId(callId); |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = callId; |
| args.arg2 = disconnectMessage; |
| args.argi1 = disconnectCause; |
| mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget(); |
| } |
| |
| @Override |
| public void setOnHold(String callId) { |
| logIncoming("setOnHold %s", callId); |
| mCallIdMapper.checkValidCallId(callId); |
| mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget(); |
| } |
| |
| @Override |
| public void setRequestingRingback(String callId, boolean ringback) { |
| logIncoming("setRequestingRingback %s %b", callId, ringback); |
| mCallIdMapper.checkValidCallId(callId); |
| mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, ringback ? 1 : 0, 0, callId) |
| .sendToTarget(); |
| } |
| |
| @Override |
| public void removeCall(String callId) { |
| logIncoming("removeCall %s", callId); |
| } |
| |
| @Override |
| public void setCallCapabilities(String callId, int callCapabilities) { |
| logIncoming("setCallCapabilities %s %d", callId, callCapabilities); |
| mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, callCapabilities, 0, callId) |
| .sendToTarget(); |
| } |
| |
| @Override |
| public void setIsConferenced(String callId, String conferenceCallId) { |
| logIncoming("setIsConferenced %s %s", callId, 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) { |
| logIncoming("addConferenceCall %s", callId); |
| mCallIdMapper.checkValidCallId(callId); |
| mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, callId).sendToTarget(); |
| } |
| |
| @Override |
| public void onPostDialWait(String callId, String remaining) throws RemoteException { |
| logIncoming("onPostDialWait %s %s", callId, remaining); |
| mCallIdMapper.checkValidCallId(callId); |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = callId; |
| args.arg2 = remaining; |
| mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget(); |
| } |
| |
| @Override |
| public void queryRemoteConnectionServices(RemoteServiceCallback callback) { |
| logIncoming("queryRemoteCSs"); |
| mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget(); |
| } |
| |
| @Override |
| public void setVideoState(String callId, int videoState) { |
| logIncoming("setVideoState %s %d", callId, videoState); |
| mCallIdMapper.checkValidCallId(callId); |
| mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, callId).sendToTarget(); |
| } |
| |
| @Override |
| public void setAudioModeIsVoip(String callId, boolean isVoip) { |
| logIncoming("setAudioModeIsVoip %s %b", callId, isVoip); |
| mCallIdMapper.checkValidCallId(callId); |
| mHandler.obtainMessage(MSG_SET_AUDIO_MODE_IS_VOIP, isVoip ? 1 : 0, 0, |
| callId).sendToTarget(); |
| } |
| |
| @Override |
| public void setStatusHints(String callId, StatusHints statusHints) { |
| logIncoming("setStatusHints %s %s", callId, statusHints); |
| mCallIdMapper.checkValidCallId(callId); |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = callId; |
| args.arg2 = statusHints; |
| mHandler.obtainMessage(MSG_SET_STATUS_HINTS, args).sendToTarget(); |
| } |
| |
| @Override |
| public void setHandle(String callId, Uri handle, int presentation) { |
| logIncoming("setHandle %s %s %d", callId, handle, presentation); |
| mCallIdMapper.checkValidCallId(callId); |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = callId; |
| args.arg2 = handle; |
| args.argi1 = presentation; |
| mHandler.obtainMessage(MSG_SET_HANDLE, args).sendToTarget(); |
| } |
| |
| @Override |
| public void setCallerDisplayName( |
| String callId, String callerDisplayName, int presentation) { |
| logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName, presentation); |
| mCallIdMapper.checkValidCallId(callId); |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = callId; |
| args.arg2 = callerDisplayName; |
| args.argi1 = presentation; |
| mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget(); |
| } |
| |
| @Override |
| public void startActivityFromInCall(String callId, PendingIntent intent) { |
| logIncoming("startActivityFromInCall %s %s", callId, intent); |
| mCallIdMapper.checkValidCallId(callId); |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = callId; |
| args.arg2 = intent; |
| mHandler.obtainMessage(MSG_START_ACTIVITY_FROM_IN_CALL, args).sendToTarget(); |
| } |
| } |
| |
| private final Adapter mAdapter = new Adapter(); |
| private final CallsManager mCallsManager = CallsManager.getInstance(); |
| private final Set<Call> mPendingConferenceCalls = new HashSet<>(); |
| private final CallIdMapper mCallIdMapper = new CallIdMapper("ConnectionService"); |
| private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>(); |
| |
| private Binder mBinder = new Binder(); |
| private IConnectionService mServiceInterface; |
| private final ConnectionServiceRepository mConnectionServiceRepository; |
| |
| /** |
| * Creates a connection service. |
| * |
| * @param componentName The component name of the service with which to bind. |
| * @param connectionServiceRepository Connection service repository. |
| */ |
| ConnectionServiceWrapper( |
| ComponentName componentName, ConnectionServiceRepository connectionServiceRepository) { |
| super(ConnectionService.SERVICE_INTERFACE, componentName); |
| mConnectionServiceRepository = connectionServiceRepository; |
| } |
| |
| /** See {@link IConnectionService#addConnectionServiceAdapter}. */ |
| private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) { |
| if (isServiceValid("addConnectionServiceAdapter")) { |
| try { |
| logOutgoing("addConnectionServiceAdapter %s", adapter); |
| mServiceInterface.addConnectionServiceAdapter(adapter); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| |
| /** |
| * Creates a new connection for a new outgoing call or to attach to an existing incoming call. |
| */ |
| void createConnection(final Call call, final CreateConnectionResponse response) { |
| Log.d(this, "createConnection(%s) via %s.", call, getComponentName()); |
| BindCallback callback = new BindCallback() { |
| @Override |
| public void onSuccess() { |
| String callId = mCallIdMapper.getCallId(call); |
| mPendingResponses.put(callId, response); |
| |
| GatewayInfo gatewayInfo = call.getGatewayInfo(); |
| Bundle extras = call.getExtras(); |
| if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null && |
| gatewayInfo.getOriginalHandle() != null) { |
| extras = (Bundle) extras.clone(); |
| extras.putString( |
| NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_PROVIDER_PACKAGE, |
| gatewayInfo.getGatewayProviderPackageName()); |
| extras.putParcelable( |
| NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_ORIGINAL_URI, |
| gatewayInfo.getOriginalHandle()); |
| } |
| ConnectionRequest request = new ConnectionRequest( |
| call.getPhoneAccount(), |
| callId, |
| call.getHandle(), |
| call.getHandlePresentation(), |
| extras, |
| call.getVideoState()); |
| |
| try { |
| mServiceInterface.createConnection(request, call.isIncoming()); |
| } catch (RemoteException e) { |
| Log.e(this, e, "Failure to createConnection -- %s", getComponentName()); |
| mPendingResponses.remove(callId).handleCreateConnectionFailed( |
| DisconnectCause.OUTGOING_FAILURE, e.toString()); |
| } |
| } |
| |
| @Override |
| public void onFailure() { |
| Log.e(this, new Exception(), "Failure to call %s", getComponentName()); |
| response.handleCreateConnectionFailed(DisconnectCause.OUTGOING_FAILURE, null); |
| } |
| }; |
| |
| mBinder.bind(callback); |
| } |
| |
| /** @see ConnectionService#abort(String) */ |
| void abort(Call call) { |
| // Clear out any pending outgoing call data |
| String callId = mCallIdMapper.getCallId(call); |
| |
| // If still bound, tell the connection service to abort. |
| if (isServiceValid("abort")) { |
| try { |
| logOutgoing("abort %s", callId); |
| mServiceInterface.abort(callId); |
| } catch (RemoteException e) { |
| } |
| } |
| |
| removeCall(call, DisconnectCause.LOCAL, null); |
| } |
| |
| /** @see ConnectionService#hold(String) */ |
| void hold(Call call) { |
| if (isServiceValid("hold")) { |
| try { |
| logOutgoing("hold %s", mCallIdMapper.getCallId(call)); |
| mServiceInterface.hold(mCallIdMapper.getCallId(call)); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| |
| /** @see ConnectionService#unhold(String) */ |
| void unhold(Call call) { |
| if (isServiceValid("unhold")) { |
| try { |
| logOutgoing("unhold %s", mCallIdMapper.getCallId(call)); |
| mServiceInterface.unhold(mCallIdMapper.getCallId(call)); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| |
| /** @see ConnectionService#onAudioStateChanged(String,CallAudioState) */ |
| void onAudioStateChanged(Call activeCall, CallAudioState audioState) { |
| if (isServiceValid("onAudioStateChanged")) { |
| try { |
| logOutgoing("onAudioStateChanged %s %s", |
| mCallIdMapper.getCallId(activeCall), audioState); |
| mServiceInterface.onAudioStateChanged(mCallIdMapper.getCallId(activeCall), |
| audioState); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| |
| /** @see ConnectionService#disconnect(String) */ |
| void disconnect(Call call) { |
| if (isServiceValid("disconnect")) { |
| try { |
| logOutgoing("disconnect %s", mCallIdMapper.getCallId(call)); |
| mServiceInterface.disconnect(mCallIdMapper.getCallId(call)); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| |
| /** @see ConnectionService#answer(String) */ |
| void answer(Call call, int videoState) { |
| if (isServiceValid("answer")) { |
| try { |
| logOutgoing("answer %s %d", mCallIdMapper.getCallId(call), videoState); |
| mServiceInterface.answer(mCallIdMapper.getCallId(call), videoState); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| |
| /** @see ConnectionService#reject(String) */ |
| void reject(Call call) { |
| if (isServiceValid("reject")) { |
| try { |
| logOutgoing("reject %s", mCallIdMapper.getCallId(call)); |
| mServiceInterface.reject(mCallIdMapper.getCallId(call)); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| |
| /** @see ConnectionService#playDtmfTone(String,char) */ |
| void playDtmfTone(Call call, char digit) { |
| if (isServiceValid("playDtmfTone")) { |
| try { |
| logOutgoing("playDtmfTone %s %c", mCallIdMapper.getCallId(call), digit); |
| mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| |
| /** @see ConnectionService#stopDtmfTone(String) */ |
| void stopDtmfTone(Call call) { |
| if (isServiceValid("stopDtmfTone")) { |
| try { |
| logOutgoing("stopDtmfTone %s", mCallIdMapper.getCallId(call)); |
| mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call)); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| |
| void addCall(Call call) { |
| if (mCallIdMapper.getCallId(call) == null) { |
| mCallIdMapper.addCall(call); |
| } |
| } |
| |
| /** |
| * Associates newCall with this connection service by replacing callToReplace. |
| */ |
| void replaceCall(Call newCall, Call callToReplace) { |
| Preconditions.checkState(callToReplace.getConnectionService() == this); |
| mCallIdMapper.replaceCall(newCall, callToReplace); |
| } |
| |
| void removeCall(Call call) { |
| removeCall(call, DisconnectCause.ERROR_UNSPECIFIED, null); |
| } |
| |
| void removeCall(Call call, int disconnectCause, String disconnectMessage) { |
| CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call)); |
| if (response != null) { |
| response.handleCreateConnectionFailed(disconnectCause, disconnectMessage); |
| } |
| |
| mCallIdMapper.removeCall(call); |
| } |
| |
| void onPostDialContinue(Call call, boolean proceed) { |
| if (isServiceValid("onPostDialContinue")) { |
| try { |
| logOutgoing("onPostDialContinue %s %b", mCallIdMapper.getCallId(call), proceed); |
| mServiceInterface.onPostDialContinue(mCallIdMapper.getCallId(call), proceed); |
| } catch (RemoteException ignored) { |
| } |
| } |
| } |
| |
| void onPhoneAccountClicked(Call call) { |
| if (isServiceValid("onPhoneAccountClicked")) { |
| try { |
| logOutgoing("onPhoneAccountClicked %s", mCallIdMapper.getCallId(call)); |
| mServiceInterface.onPhoneAccountClicked(mCallIdMapper.getCallId(call)); |
| } catch (RemoteException ignored) { |
| } |
| } |
| } |
| |
| void conference(final Call conferenceCall, Call call) { |
| if (isServiceValid("conference")) { |
| try { |
| conferenceCall.setConnectionService(this); |
| mPendingConferenceCalls.add(conferenceCall); |
| mHandler.postDelayed(new Runnable() { |
| @Override public void run() { |
| if (mPendingConferenceCalls.remove(conferenceCall)) { |
| conferenceCall.expireConference(); |
| Log.i(this, "Conference call expired: %s", conferenceCall); |
| } |
| } |
| }, Timeouts.getConferenceCallExpireMillis()); |
| |
| logOutgoing("conference %s %s", |
| mCallIdMapper.getCallId(conferenceCall), |
| mCallIdMapper.getCallId(call)); |
| mServiceInterface.conference( |
| mCallIdMapper.getCallId(conferenceCall), |
| mCallIdMapper.getCallId(call)); |
| } catch (RemoteException ignored) { |
| } |
| } |
| } |
| |
| void splitFromConference(Call call) { |
| if (isServiceValid("splitFromConference")) { |
| try { |
| logOutgoing("splitFromConference %s", mCallIdMapper.getCallId(call)); |
| mServiceInterface.splitFromConference(mCallIdMapper.getCallId(call)); |
| } catch (RemoteException ignored) { |
| } |
| } |
| } |
| |
| void swapWithBackgroundCall(Call call) { |
| if (isServiceValid("swapWithBackgroundCall")) { |
| try { |
| logOutgoing("swapWithBackgroundCall %s", mCallIdMapper.getCallId(call)); |
| mServiceInterface.swapWithBackgroundCall(mCallIdMapper.getCallId(call)); |
| } catch (RemoteException ignored) { |
| } |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| protected void setServiceInterface(IBinder binder) { |
| if (binder == null) { |
| // We have lost our service connection. Notify the world that this service is done. |
| // We must notify the adapter before CallsManager. The adapter will force any pending |
| // outgoing calls to try the next service. This needs to happen before CallsManager |
| // tries to clean up any calls still associated with this service. |
| handleConnectionServiceDeath(); |
| CallsManager.getInstance().handleConnectionServiceDeath(this); |
| mServiceInterface = null; |
| } else { |
| mServiceInterface = IConnectionService.Stub.asInterface(binder); |
| addConnectionServiceAdapter(mAdapter); |
| } |
| } |
| |
| /** |
| * Called when the associated connection service dies. |
| */ |
| private void handleConnectionServiceDeath() { |
| if (!mPendingResponses.isEmpty()) { |
| CreateConnectionResponse[] responses = mPendingResponses.values().toArray( |
| new CreateConnectionResponse[mPendingResponses.values().size()]); |
| mPendingResponses.clear(); |
| for (int i = 0; i < responses.length; i++) { |
| responses[i].handleCreateConnectionFailed(DisconnectCause.ERROR_UNSPECIFIED, null); |
| } |
| } |
| mCallIdMapper.clear(); |
| } |
| |
| private void logIncoming(String msg, Object... params) { |
| Log.d(this, "ConnectionService -> Telecomm: " + msg, params); |
| } |
| |
| private void logOutgoing(String msg, Object... params) { |
| Log.d(this, "Telecomm -> ConnectionService: " + msg, params); |
| } |
| |
| private void queryRemoteConnectionServices(final RemoteServiceCallback callback) { |
| final List<IBinder> connectionServices = new ArrayList<>(); |
| final List<ComponentName> components = new ArrayList<>(); |
| final List<ConnectionServiceWrapper> servciesAttempted = new ArrayList<>(); |
| final Collection<ConnectionServiceWrapper> services = |
| mConnectionServiceRepository.lookupServices(); |
| |
| Log.v(this, "queryRemoteConnectionServices, services: " + services.size()); |
| |
| for (ConnectionServiceWrapper cs : services) { |
| if (cs != this) { |
| final ConnectionServiceWrapper currentConnectionService = cs; |
| cs.mBinder.bind(new BindCallback() { |
| @Override |
| public void onSuccess() { |
| Log.d(this, "Adding ***** %s", currentConnectionService.getComponentName()); |
| connectionServices.add( |
| currentConnectionService.mServiceInterface.asBinder()); |
| components.add(currentConnectionService.getComponentName()); |
| maybeComplete(); |
| } |
| |
| @Override |
| public void onFailure() { |
| maybeComplete(); |
| } |
| |
| private void maybeComplete() { |
| servciesAttempted.add(currentConnectionService); |
| if (servciesAttempted.size() == services.size() - 1) { |
| try { |
| callback.onResult(components, connectionServices); |
| } catch (RemoteException ignored) { |
| } |
| } |
| } |
| }); |
| } |
| } |
| } |
| } |