Loading src/com/android/server/telecom/Call.java +22 −0 Original line number Diff line number Diff line Loading @@ -138,6 +138,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy); void onHandoverFailed(Call call, int error); void onHandoverComplete(Call call); } public abstract static class ListenerBase implements Listener { Loading Loading @@ -214,6 +215,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, Bundle extras, boolean isLegacy) {} @Override public void onHandoverFailed(Call call, int error) {} @Override public void onHandoverComplete(Call call) {} } private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener = Loading Loading @@ -2778,7 +2781,26 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, } } /** * Notifies interested parties that the handover has completed. * Notifies: * 1. {@link InCallController} which communicates this to the * {@link android.telecom.InCallService} via {@link Listener#onHandoverComplete()}. * 2. {@link ConnectionServiceWrapper} which informs the {@link android.telecom.Connection} of * the successful handover. */ public void onHandoverComplete() { Log.i(this, "onHandoverComplete; callId=%s", getId()); if (mConnectionService != null) { mConnectionService.handoverComplete(this); } for (Listener l : mListeners) { l.onHandoverComplete(this); } } public void onHandoverFailed(int handoverError) { Log.i(this, "onHandoverFailed; callId=%s, handoverError=%d", getId(), handoverError); for (Listener l : mListeners) { l.onHandoverFailed(this, handoverError); } Loading src/com/android/server/telecom/CallsManager.java +30 −10 Original line number Diff line number Diff line Loading @@ -2409,8 +2409,11 @@ public class CallsManager extends Call.ListenerBase // completed; this allows the InCallService to be notified that a handover it // initiated completed. call.onConnectionEvent(Connection.EVENT_HANDOVER_COMPLETE, null); call.onHandoverComplete(); // Inform the "to" ConnectionService that handover to it has completed. handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null); handoverTo.onHandoverComplete(); answerCall(handoverTo, handoverTo.getVideoState()); call.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_COMPLETE); Loading Loading @@ -3221,8 +3224,12 @@ public class CallsManager extends Call.ListenerBase } private void setIntentExtrasAndStartTime(Call call, Bundle extras) { if (extras != null) { // Create our own instance to modify (since extras may be Bundle.EMPTY) extras = new Bundle(extras); } else { extras = new Bundle(); } // Specifies the time telecom began routing the call. This is used by the dialer for // analytics. Loading Loading @@ -3266,7 +3273,6 @@ public class CallsManager extends Call.ListenerBase service.handoverFailed(call, reason); call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED)); call.disconnect(); } /** Loading Loading @@ -3332,7 +3338,7 @@ public class CallsManager extends Call.ListenerBase * @param handoverFromCall The {@link Call} to be handed over. * @param handoverToHandle The {@link PhoneAccountHandle} to hand over the call to. * @param videoState The desired video state of {@link Call} after handover. * @param initiatingExtras Extras associated with the handover, to be passed to the handover * @param extras Extras associated with the handover, to be passed to the handover * {@link android.telecom.ConnectionService}. */ private void requestHandover(Call handoverFromCall, PhoneAccountHandle handoverToHandle, Loading Loading @@ -3378,8 +3384,6 @@ public class CallsManager extends Call.ListenerBase } call.setInitiatingUser(getCurrentUserHandle()); // Ensure we don't try to place an outgoing call with video if video is not // supported. if (VideoProfile.isVideo(videoState) && account != null && Loading @@ -3403,6 +3407,14 @@ public class CallsManager extends Call.ListenerBase call.setState( CallState.CONNECTING, handoverToHandle == null ? "no-handle" : handoverToHandle.toString()); // Mark as handover so that the ConnectionService knows this is a handover request. if (extras == null) { extras = new Bundle(); } extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true); extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, handoverFromCall.getTargetPhoneAccount()); setIntentExtrasAndStartTime(call, extras); // Add call to call tracker Loading Loading @@ -3613,6 +3625,14 @@ public class CallsManager extends Call.ListenerBase call.setStartWithSpeakerphoneOn(true); } Bundle extras = call.getIntentExtras(); if (extras == null) { extras = new Bundle(); } extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true); extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, fromCall.getTargetPhoneAccount()); call.startCreateConnection(mPhoneAccountRegistrar); } Loading src/com/android/server/telecom/ConnectionServiceWrapper.java +28 −0 Original line number Diff line number Diff line Loading @@ -1106,6 +1106,34 @@ public class ConnectionServiceWrapper extends ServiceBinder implements mBinder.bind(callback, call); } void handoverComplete(final Call call) { Log.d(this, "handoverComplete(%s) via %s.", call, getComponentName()); BindCallback callback = new BindCallback() { @Override public void onSuccess() { final String callId = mCallIdMapper.getCallId(call); // If still bound, tell the connection service create connection has failed. if (callId != null && isServiceValid("handoverComplete")) { try { mServiceInterface.handoverComplete( callId, Log.getExternalSession()); } catch (RemoteException e) { } } } @Override public void onFailure() { // Binding failed. Log.w(this, "onFailure - could not bind to CS for call %s", call.getId()); } }; mBinder.bind(callback, call); } /** @see IConnectionService#abort(String, Session.Info) */ void abort(Call call) { // Clear out any pending outgoing call data Loading src/com/android/server/telecom/InCallController.java +16 −0 Original line number Diff line number Diff line Loading @@ -681,6 +681,11 @@ public class InCallController extends CallsManagerListenerBase { notifyHandoverFailed(call, error); } @Override public void onHandoverComplete(Call call) { notifyHandoverComplete(call); } @Override public void onRttInitiationFailure(Call call, int reason) { notifyRttInitiationFailure(call, reason); Loading Loading @@ -1037,6 +1042,17 @@ public class InCallController extends CallsManagerListenerBase { } } private void notifyHandoverComplete(Call call) { if (!mInCallServices.isEmpty()) { for (IInCallService inCallService : mInCallServices.values()) { try { inCallService.onHandoverComplete(mCallIdMapper.getCallId(call)); } catch (RemoteException ignored) { } } } } /** * Unbinds an existing bound connection to the in-call app. */ Loading src/com/android/server/telecom/TelecomServiceImpl.java +33 −2 Original line number Diff line number Diff line Loading @@ -1083,8 +1083,10 @@ public class TelecomServiceImpl { try { Log.startSession("TSI.aHO"); synchronized (mLock) { Log.i(this, "Handover call to phoneAccountHandle %s", Log.i(this, "acceptHandover; srcAddr=%s, videoState=%s, dest=%s", Log.pii(srcAddr), VideoProfile.videoStateToString(videoState), destAcct); if (destAcct != null && destAcct.getComponentName() != null) { mAppOpsManager.checkPackage( Binder.getCallingUid(), Loading @@ -1099,8 +1101,19 @@ public class TelecomServiceImpl { "Self-managed phone accounts must have MANAGE_OWN_CALLS " + "permission."); } if (!enforceAcceptHandoverPermission( destAcct.getComponentName().getPackageName(), Binder.getCallingUid())) { throw new SecurityException("App must be granted runtime " + "ACCEPT_HANDOVER permission."); } long token = Binder.clearCallingIdentity(); try { mCallsManager.acceptHandover(srcAddr, videoState, destAcct); } finally { Binder.restoreCallingIdentity(token); } } else { Log.w(this, "Null phoneAccountHandle. Ignoring request " + "to handover the call"); Loading Loading @@ -1443,6 +1456,24 @@ public class TelecomServiceImpl { return true; } /** * @return {@code true} if the app has the handover permission and has received runtime * permission to perform that operation, {@code false}. * @throws SecurityException same as {@link Context#enforceCallingOrSelfPermission} */ private boolean enforceAcceptHandoverPermission(String packageName, int uid) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCEPT_HANDOVER, "App requires ACCEPT_HANDOVER permission to accept handovers."); final int opCode = AppOpsManager.permissionToOpCode(Manifest.permission.ACCEPT_HANDOVER); if (opCode != AppOpsManager.OP_ACCEPT_HANDOVER || ( mAppOpsManager.checkOp(opCode, uid, packageName) != AppOpsManager.MODE_ALLOWED)) { return false; } return true; } private Context mContext; private AppOpsManager mAppOpsManager; private PackageManager mPackageManager; Loading Loading
src/com/android/server/telecom/Call.java +22 −0 Original line number Diff line number Diff line Loading @@ -138,6 +138,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy); void onHandoverFailed(Call call, int error); void onHandoverComplete(Call call); } public abstract static class ListenerBase implements Listener { Loading Loading @@ -214,6 +215,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, Bundle extras, boolean isLegacy) {} @Override public void onHandoverFailed(Call call, int error) {} @Override public void onHandoverComplete(Call call) {} } private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener = Loading Loading @@ -2778,7 +2781,26 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, } } /** * Notifies interested parties that the handover has completed. * Notifies: * 1. {@link InCallController} which communicates this to the * {@link android.telecom.InCallService} via {@link Listener#onHandoverComplete()}. * 2. {@link ConnectionServiceWrapper} which informs the {@link android.telecom.Connection} of * the successful handover. */ public void onHandoverComplete() { Log.i(this, "onHandoverComplete; callId=%s", getId()); if (mConnectionService != null) { mConnectionService.handoverComplete(this); } for (Listener l : mListeners) { l.onHandoverComplete(this); } } public void onHandoverFailed(int handoverError) { Log.i(this, "onHandoverFailed; callId=%s, handoverError=%d", getId(), handoverError); for (Listener l : mListeners) { l.onHandoverFailed(this, handoverError); } Loading
src/com/android/server/telecom/CallsManager.java +30 −10 Original line number Diff line number Diff line Loading @@ -2409,8 +2409,11 @@ public class CallsManager extends Call.ListenerBase // completed; this allows the InCallService to be notified that a handover it // initiated completed. call.onConnectionEvent(Connection.EVENT_HANDOVER_COMPLETE, null); call.onHandoverComplete(); // Inform the "to" ConnectionService that handover to it has completed. handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null); handoverTo.onHandoverComplete(); answerCall(handoverTo, handoverTo.getVideoState()); call.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_COMPLETE); Loading Loading @@ -3221,8 +3224,12 @@ public class CallsManager extends Call.ListenerBase } private void setIntentExtrasAndStartTime(Call call, Bundle extras) { if (extras != null) { // Create our own instance to modify (since extras may be Bundle.EMPTY) extras = new Bundle(extras); } else { extras = new Bundle(); } // Specifies the time telecom began routing the call. This is used by the dialer for // analytics. Loading Loading @@ -3266,7 +3273,6 @@ public class CallsManager extends Call.ListenerBase service.handoverFailed(call, reason); call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED)); call.disconnect(); } /** Loading Loading @@ -3332,7 +3338,7 @@ public class CallsManager extends Call.ListenerBase * @param handoverFromCall The {@link Call} to be handed over. * @param handoverToHandle The {@link PhoneAccountHandle} to hand over the call to. * @param videoState The desired video state of {@link Call} after handover. * @param initiatingExtras Extras associated with the handover, to be passed to the handover * @param extras Extras associated with the handover, to be passed to the handover * {@link android.telecom.ConnectionService}. */ private void requestHandover(Call handoverFromCall, PhoneAccountHandle handoverToHandle, Loading Loading @@ -3378,8 +3384,6 @@ public class CallsManager extends Call.ListenerBase } call.setInitiatingUser(getCurrentUserHandle()); // Ensure we don't try to place an outgoing call with video if video is not // supported. if (VideoProfile.isVideo(videoState) && account != null && Loading @@ -3403,6 +3407,14 @@ public class CallsManager extends Call.ListenerBase call.setState( CallState.CONNECTING, handoverToHandle == null ? "no-handle" : handoverToHandle.toString()); // Mark as handover so that the ConnectionService knows this is a handover request. if (extras == null) { extras = new Bundle(); } extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true); extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, handoverFromCall.getTargetPhoneAccount()); setIntentExtrasAndStartTime(call, extras); // Add call to call tracker Loading Loading @@ -3613,6 +3625,14 @@ public class CallsManager extends Call.ListenerBase call.setStartWithSpeakerphoneOn(true); } Bundle extras = call.getIntentExtras(); if (extras == null) { extras = new Bundle(); } extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true); extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, fromCall.getTargetPhoneAccount()); call.startCreateConnection(mPhoneAccountRegistrar); } Loading
src/com/android/server/telecom/ConnectionServiceWrapper.java +28 −0 Original line number Diff line number Diff line Loading @@ -1106,6 +1106,34 @@ public class ConnectionServiceWrapper extends ServiceBinder implements mBinder.bind(callback, call); } void handoverComplete(final Call call) { Log.d(this, "handoverComplete(%s) via %s.", call, getComponentName()); BindCallback callback = new BindCallback() { @Override public void onSuccess() { final String callId = mCallIdMapper.getCallId(call); // If still bound, tell the connection service create connection has failed. if (callId != null && isServiceValid("handoverComplete")) { try { mServiceInterface.handoverComplete( callId, Log.getExternalSession()); } catch (RemoteException e) { } } } @Override public void onFailure() { // Binding failed. Log.w(this, "onFailure - could not bind to CS for call %s", call.getId()); } }; mBinder.bind(callback, call); } /** @see IConnectionService#abort(String, Session.Info) */ void abort(Call call) { // Clear out any pending outgoing call data Loading
src/com/android/server/telecom/InCallController.java +16 −0 Original line number Diff line number Diff line Loading @@ -681,6 +681,11 @@ public class InCallController extends CallsManagerListenerBase { notifyHandoverFailed(call, error); } @Override public void onHandoverComplete(Call call) { notifyHandoverComplete(call); } @Override public void onRttInitiationFailure(Call call, int reason) { notifyRttInitiationFailure(call, reason); Loading Loading @@ -1037,6 +1042,17 @@ public class InCallController extends CallsManagerListenerBase { } } private void notifyHandoverComplete(Call call) { if (!mInCallServices.isEmpty()) { for (IInCallService inCallService : mInCallServices.values()) { try { inCallService.onHandoverComplete(mCallIdMapper.getCallId(call)); } catch (RemoteException ignored) { } } } } /** * Unbinds an existing bound connection to the in-call app. */ Loading
src/com/android/server/telecom/TelecomServiceImpl.java +33 −2 Original line number Diff line number Diff line Loading @@ -1083,8 +1083,10 @@ public class TelecomServiceImpl { try { Log.startSession("TSI.aHO"); synchronized (mLock) { Log.i(this, "Handover call to phoneAccountHandle %s", Log.i(this, "acceptHandover; srcAddr=%s, videoState=%s, dest=%s", Log.pii(srcAddr), VideoProfile.videoStateToString(videoState), destAcct); if (destAcct != null && destAcct.getComponentName() != null) { mAppOpsManager.checkPackage( Binder.getCallingUid(), Loading @@ -1099,8 +1101,19 @@ public class TelecomServiceImpl { "Self-managed phone accounts must have MANAGE_OWN_CALLS " + "permission."); } if (!enforceAcceptHandoverPermission( destAcct.getComponentName().getPackageName(), Binder.getCallingUid())) { throw new SecurityException("App must be granted runtime " + "ACCEPT_HANDOVER permission."); } long token = Binder.clearCallingIdentity(); try { mCallsManager.acceptHandover(srcAddr, videoState, destAcct); } finally { Binder.restoreCallingIdentity(token); } } else { Log.w(this, "Null phoneAccountHandle. Ignoring request " + "to handover the call"); Loading Loading @@ -1443,6 +1456,24 @@ public class TelecomServiceImpl { return true; } /** * @return {@code true} if the app has the handover permission and has received runtime * permission to perform that operation, {@code false}. * @throws SecurityException same as {@link Context#enforceCallingOrSelfPermission} */ private boolean enforceAcceptHandoverPermission(String packageName, int uid) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCEPT_HANDOVER, "App requires ACCEPT_HANDOVER permission to accept handovers."); final int opCode = AppOpsManager.permissionToOpCode(Manifest.permission.ACCEPT_HANDOVER); if (opCode != AppOpsManager.OP_ACCEPT_HANDOVER || ( mAppOpsManager.checkOp(opCode, uid, packageName) != AppOpsManager.MODE_ALLOWED)) { return false; } return true; } private Context mContext; private AppOpsManager mAppOpsManager; private PackageManager mPackageManager; Loading