Loading core/proto/android/companion/telecom.proto +36 −6 Original line number Diff line number Diff line Loading @@ -31,11 +31,8 @@ message Telecom { // Caller's name and/or phone number; what a user would see displayed when receiving an // incoming call on the local device string caller_id = 1; // Human-readable name of the app processing this call string app_name = 2; bytes app_icon = 3; // Unique identifier for this app, such as a package name. string app_identifier = 4; bytes app_icon = 2; CallFacilitator facilitator = 3; } Origin origin = 2; Loading @@ -51,6 +48,37 @@ message Telecom { repeated Control controls = 4; } message Request { message CreateAction { // UUID representing this request. string id = 1; // URI representing the address of the intended callee. string address = 2; // Which facilitator should handle this call. CallFacilitator facilitator = 3; } message ControlAction { // UUID representing the call to perform the control action on string id = 1; // The control to perform Control control = 2; } oneof action { CreateAction create_action = 1; ControlAction control_action = 2; } } // A facilitator (namely an app) that can be directed to place calls. // Next index: 3 message CallFacilitator { // Human-readable name of the facilitator string name = 1; // Unique identifier for this facilitator, such as a package name. string identifier = 2; } enum Control { UNKNOWN_CONTROL = 0; ACCEPT = 1; Loading @@ -68,5 +96,7 @@ message Telecom { // The list of active calls. repeated Call calls = 1; // The list of requested calls or call changes. repeated Call requests = 2; repeated Request requests = 2; // The list of call facilitators that this device currently supports. repeated CallFacilitator facilitators = 3; } services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java +129 −73 Original line number Diff line number Diff line Loading @@ -16,18 +16,21 @@ package com.android.server.companion.datatransfer.contextsync; import android.content.ComponentName; import android.media.AudioManager; import android.os.Bundle; import android.telecom.Call; import android.telecom.Connection; import android.telecom.ConnectionRequest; import android.telecom.ConnectionService; import android.telecom.DisconnectCause; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.companion.CompanionDeviceManagerServiceInternal; import java.util.HashMap; import java.util.Map; Loading @@ -39,10 +42,47 @@ public class CallMetadataSyncConnectionService extends ConnectionService { private static final String TAG = "CallMetadataSyncConnectionService"; private AudioManager mAudioManager; private TelecomManager mTelecomManager; private final Map<PhoneAccountHandleIdentifier, PhoneAccountHandle> mPhoneAccountHandles = new HashMap<>(); @VisibleForTesting AudioManager mAudioManager; @VisibleForTesting TelecomManager mTelecomManager; private CompanionDeviceManagerServiceInternal mCdmsi; @VisibleForTesting final Map<CallMetadataSyncConnectionIdentifier, CallMetadataSyncConnection> mActiveConnections = new HashMap<>(); @VisibleForTesting final CrossDeviceSyncControllerCallback mCrossDeviceSyncControllerCallback = new CrossDeviceSyncControllerCallback() { @Override void processContextSyncMessage(int associationId, CallMetadataSyncData callMetadataSyncData) { // Add new calls or update existing calls. for (CallMetadataSyncData.Call call : callMetadataSyncData.getCalls()) { final CallMetadataSyncConnection existingConnection = mActiveConnections.get(new CallMetadataSyncConnectionIdentifier( associationId, call.getId())); if (existingConnection == null) { final Bundle extras = new Bundle(); extras.putInt(CrossDeviceSyncController.EXTRA_ASSOCIATION_ID, associationId); extras.putParcelable(CrossDeviceSyncController.EXTRA_CALL, call); mTelecomManager.addNewIncomingCall(call.getPhoneAccountHandle(), extras); } else { existingConnection.update(call); } } // Remove obsolete calls. mActiveConnections.values().removeIf(connection -> { if (!callMetadataSyncData.hasCall(connection.getCallId())) { connection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE)); return true; } return false; }); } }; @Override public void onCreate() { Loading @@ -50,83 +90,96 @@ public class CallMetadataSyncConnectionService extends ConnectionService { mAudioManager = getSystemService(AudioManager.class); mTelecomManager = getSystemService(TelecomManager.class); mCdmsi = LocalServices.getService(CompanionDeviceManagerServiceInternal.class); mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback); } /** * Registers a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced * device. */ private void registerPhoneAccount(int associationId, String appIdentifier, String humanReadableAppName) { final PhoneAccountHandleIdentifier phoneAccountHandleIdentifier = new PhoneAccountHandleIdentifier(associationId, appIdentifier); final PhoneAccount phoneAccount = createPhoneAccount(phoneAccountHandleIdentifier, humanReadableAppName); mTelecomManager.registerPhoneAccount(phoneAccount); mTelecomManager.enablePhoneAccount(mPhoneAccountHandles.get(phoneAccountHandleIdentifier), true); } /** * Unregisters a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced * device. */ private void unregisterPhoneAccount(int associationId, String appIdentifier) { mTelecomManager.unregisterPhoneAccount(mPhoneAccountHandles.remove( new PhoneAccountHandleIdentifier(associationId, appIdentifier))); @Override public Connection onCreateIncomingConnection(PhoneAccountHandle phoneAccountHandle, ConnectionRequest connectionRequest) { final int associationId = connectionRequest.getExtras().getInt( CrossDeviceSyncController.EXTRA_ASSOCIATION_ID); final CallMetadataSyncData.Call call = connectionRequest.getExtras().getParcelable( CrossDeviceSyncController.EXTRA_CALL, CallMetadataSyncData.Call.class); final CallMetadataSyncConnection connection = new CallMetadataSyncConnection( mTelecomManager, mAudioManager, associationId, call, new CallMetadataSyncConnectionCallback() { @Override void sendCallAction(int associationId, String callId, int action) { mCdmsi.sendCrossDeviceSyncMessage(associationId, CrossDeviceSyncController.createCallControlMessage(callId, action)); } }); connection.setConnectionProperties( Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED); return connection; } @VisibleForTesting PhoneAccount createPhoneAccount(PhoneAccountHandleIdentifier phoneAccountHandleIdentifier, String humanReadableAppName) { if (mPhoneAccountHandles.containsKey(phoneAccountHandleIdentifier)) { // Already exists! return null; } final PhoneAccountHandle handle = new PhoneAccountHandle( new ComponentName(this, CallMetadataSyncConnectionService.class), UUID.randomUUID().toString()); mPhoneAccountHandles.put(phoneAccountHandleIdentifier, handle); return new PhoneAccount.Builder(handle, humanReadableAppName) .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER | PhoneAccount.CAPABILITY_SELF_MANAGED).build(); } static final class PhoneAccountHandleIdentifier { private final int mAssociationId; private final String mAppIdentifier; PhoneAccountHandleIdentifier(int associationId, String appIdentifier) { mAssociationId = associationId; mAppIdentifier = appIdentifier; @Override public void onCreateIncomingConnectionFailed(PhoneAccountHandle phoneAccountHandle, ConnectionRequest connectionRequest) { Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId()); } public int getAssociationId() { return mAssociationId; @Override public Connection onCreateOutgoingConnection(PhoneAccountHandle phoneAccountHandle, ConnectionRequest connectionRequest) { final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle); final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId(UUID.randomUUID().toString()); call.setStatus(android.companion.Telecom.Call.UNKNOWN_STATUS); call.setPhoneAccountHandle(phoneAccountHandle); final CallMetadataSyncData.CallFacilitator callFacilitator = new CallMetadataSyncData.CallFacilitator(phoneAccount.getLabel().toString(), phoneAccount.getExtras().getString( CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID)); call.setFacilitator(callFacilitator); final int associationId = connectionRequest.getExtras().getInt( CrossDeviceSyncController.EXTRA_ASSOCIATION_ID); final CallMetadataSyncConnection connection = new CallMetadataSyncConnection( mTelecomManager, mAudioManager, associationId, call, new CallMetadataSyncConnectionCallback() { @Override void sendCallAction(int associationId, String callId, int action) { mCdmsi.sendCrossDeviceSyncMessage(associationId, CrossDeviceSyncController.createCallControlMessage(callId, action)); } }); connection.setConnectionProperties( Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED); public String getAppIdentifier() { return mAppIdentifier; mCdmsi.sendCrossDeviceSyncMessage(associationId, CrossDeviceSyncController.createCallCreateMessage(call.getId(), connectionRequest.getAddress().toString(), call.getFacilitator().getIdentifier())); return connection; } @Override public int hashCode() { return Objects.hash(mAssociationId, mAppIdentifier); public void onCreateOutgoingConnectionFailed(PhoneAccountHandle phoneAccountHandle, ConnectionRequest connectionRequest) { Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId()); } @Override public boolean equals(Object other) { if (other instanceof PhoneAccountHandleIdentifier) { return ((PhoneAccountHandleIdentifier) other).getAssociationId() == mAssociationId && mAppIdentifier != null && mAppIdentifier.equals( ((PhoneAccountHandleIdentifier) other).getAppIdentifier()); } return false; public void onCreateConnectionComplete(Connection connection) { if (connection instanceof CallMetadataSyncConnection) { ((CallMetadataSyncConnection) connection).initialize(); } } private static final class CallMetadataSyncConnectionIdentifier { @VisibleForTesting static final class CallMetadataSyncConnectionIdentifier { private final int mAssociationId; private final String mCallId; Loading @@ -153,18 +206,21 @@ public class CallMetadataSyncConnectionService extends ConnectionService { if (other instanceof CallMetadataSyncConnectionIdentifier) { return ((CallMetadataSyncConnectionIdentifier) other).getAssociationId() == mAssociationId && (((CallMetadataSyncConnectionIdentifier) other).getCallId() == mCallId); && mCallId != null && mCallId.equals( ((CallMetadataSyncConnectionIdentifier) other).getCallId()); } return false; } } private abstract static class CallMetadataSyncConnectionCallback { @VisibleForTesting abstract static class CallMetadataSyncConnectionCallback { abstract void sendCallAction(int associationId, String callId, int action); } private static class CallMetadataSyncConnection extends Connection { @VisibleForTesting static class CallMetadataSyncConnection extends Connection { private final TelecomManager mTelecomManager; private final AudioManager mAudioManager; Loading services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java +150 −19 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.companion.ContextSyncMessage; import android.os.Parcel; import android.os.Parcelable; import android.telecom.PhoneAccountHandle; import java.util.ArrayList; import java.util.Collection; Loading @@ -34,7 +35,9 @@ import java.util.Set; class CallMetadataSyncData { final Map<String, CallMetadataSyncData.Call> mCalls = new HashMap<>(); final List<CallMetadataSyncData.Call> mRequests = new ArrayList<>(); final List<CallMetadataSyncData.CallCreateRequest> mCallCreateRequests = new ArrayList<>(); final List<CallMetadataSyncData.CallControlRequest> mCallControlRequests = new ArrayList<>(); final List<CallMetadataSyncData.CallFacilitator> mCallFacilitators = new ArrayList<>(); public void addCall(CallMetadataSyncData.Call call) { mCalls.put(call.getId(), call); Loading @@ -48,20 +51,145 @@ class CallMetadataSyncData { return mCalls.values(); } public void addRequest(CallMetadataSyncData.Call call) { mRequests.add(call); public void addCallCreateRequest(CallMetadataSyncData.CallCreateRequest request) { mCallCreateRequests.add(request); } public List<CallMetadataSyncData.Call> getRequests() { return mRequests; public List<CallMetadataSyncData.CallCreateRequest> getCallCreateRequests() { return mCallCreateRequests; } public void addCallControlRequest(CallMetadataSyncData.CallControlRequest request) { mCallControlRequests.add(request); } public List<CallMetadataSyncData.CallControlRequest> getCallControlRequests() { return mCallControlRequests; } public void addFacilitator(CallFacilitator facilitator) { mCallFacilitators.add(facilitator); } public List<CallFacilitator> getFacilitators() { return mCallFacilitators; } public static class CallFacilitator implements Parcelable { private String mName; private String mIdentifier; CallFacilitator() {} CallFacilitator(String name, String identifier) { mName = name; mIdentifier = identifier; } CallFacilitator(Parcel parcel) { this(parcel.readString(), parcel.readString()); } @Override public void writeToParcel(Parcel parcel, int parcelableFlags) { parcel.writeString(mName); parcel.writeString(mIdentifier); } public String getName() { return mName; } public String getIdentifier() { return mIdentifier; } public void setName(String name) { mName = name; } public void setIdentifier(String identifier) { mIdentifier = identifier; } @Override public int describeContents() { return 0; } @NonNull public static final Parcelable.Creator<CallFacilitator> CREATOR = new Parcelable.Creator<>() { @Override public CallFacilitator createFromParcel(Parcel source) { return new CallFacilitator(source); } @Override public CallFacilitator[] newArray(int size) { return new CallFacilitator[size]; } }; } public static class CallControlRequest { private String mId; private int mControl; public void setId(String id) { mId = id; } public void setControl(int control) { mControl = control; } public String getId() { return mId; } public int getControl() { return mControl; } } public static class CallCreateRequest { private String mId; private String mAddress; private CallFacilitator mFacilitator; public void setId(String id) { mId = id; } public void setAddress(String address) { mAddress = address; } public void setFacilitator(CallFacilitator facilitator) { mFacilitator = facilitator; } public String getId() { return mId; } public String getAddress() { return mAddress; } public CallFacilitator getFacilitator() { return mFacilitator; } } public static class Call implements Parcelable { private String mId; private String mCallerId; private byte[] mAppIcon; private String mAppName; private String mAppIdentifier; private CallFacilitator mFacilitator; private PhoneAccountHandle mPhoneAccountHandle; private int mStatus; private final Set<Integer> mControls = new HashSet<>(); Loading @@ -70,8 +198,11 @@ class CallMetadataSyncData { call.setId(parcel.readString()); call.setCallerId(parcel.readString()); call.setAppIcon(parcel.readBlob()); call.setAppName(parcel.readString()); call.setAppIdentifier(parcel.readString()); call.setFacilitator(parcel.readParcelable(CallFacilitator.class.getClassLoader(), CallFacilitator.class)); call.setPhoneAccountHandle( parcel.readParcelable(PhoneAccountHandle.class.getClassLoader(), android.telecom.PhoneAccountHandle.class)); call.setStatus(parcel.readInt()); final int numberOfControls = parcel.readInt(); for (int i = 0; i < numberOfControls; i++) { Loading @@ -85,8 +216,8 @@ class CallMetadataSyncData { parcel.writeString(mId); parcel.writeString(mCallerId); parcel.writeBlob(mAppIcon); parcel.writeString(mAppName); parcel.writeString(mAppIdentifier); parcel.writeParcelable(mFacilitator, parcelableFlags); parcel.writeParcelable(mPhoneAccountHandle, parcelableFlags); parcel.writeInt(mStatus); parcel.writeInt(mControls.size()); for (int control : mControls) { Loading @@ -106,12 +237,12 @@ class CallMetadataSyncData { mAppIcon = appIcon; } void setAppName(String appName) { mAppName = appName; void setFacilitator(CallFacilitator facilitator) { mFacilitator = facilitator; } void setAppIdentifier(String appIdentifier) { mAppIdentifier = appIdentifier; void setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) { mPhoneAccountHandle = phoneAccountHandle; } void setStatus(int status) { Loading @@ -134,12 +265,12 @@ class CallMetadataSyncData { return mAppIcon; } String getAppName() { return mAppName; CallFacilitator getFacilitator() { return mFacilitator; } String getAppIdentifier() { return mAppIdentifier; PhoneAccountHandle getPhoneAccountHandle() { return mPhoneAccountHandle; } int getStatus() { Loading services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java +4 −9 Original line number Diff line number Diff line Loading @@ -66,16 +66,11 @@ public class CallMetadataSyncInCallService extends InCallService { @Override void processContextSyncMessage(int associationId, CallMetadataSyncData callMetadataSyncData) { final Iterator<CallMetadataSyncData.Call> iterator = callMetadataSyncData.getRequests().iterator(); final Iterator<CallMetadataSyncData.CallControlRequest> iterator = callMetadataSyncData.getCallControlRequests().iterator(); while (iterator.hasNext()) { final CallMetadataSyncData.Call call = iterator.next(); if (call.getId() != null) { // The call is already assigned an id; treat as control invocations. for (int control : call.getControls()) { processCallControlAction(call.getId(), control); } } final CallMetadataSyncData.CallControlRequest request = iterator.next(); processCallControlAction(request.getId(), request.getControl()); iterator.remove(); } } Loading services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java +319 −13 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/proto/android/companion/telecom.proto +36 −6 Original line number Diff line number Diff line Loading @@ -31,11 +31,8 @@ message Telecom { // Caller's name and/or phone number; what a user would see displayed when receiving an // incoming call on the local device string caller_id = 1; // Human-readable name of the app processing this call string app_name = 2; bytes app_icon = 3; // Unique identifier for this app, such as a package name. string app_identifier = 4; bytes app_icon = 2; CallFacilitator facilitator = 3; } Origin origin = 2; Loading @@ -51,6 +48,37 @@ message Telecom { repeated Control controls = 4; } message Request { message CreateAction { // UUID representing this request. string id = 1; // URI representing the address of the intended callee. string address = 2; // Which facilitator should handle this call. CallFacilitator facilitator = 3; } message ControlAction { // UUID representing the call to perform the control action on string id = 1; // The control to perform Control control = 2; } oneof action { CreateAction create_action = 1; ControlAction control_action = 2; } } // A facilitator (namely an app) that can be directed to place calls. // Next index: 3 message CallFacilitator { // Human-readable name of the facilitator string name = 1; // Unique identifier for this facilitator, such as a package name. string identifier = 2; } enum Control { UNKNOWN_CONTROL = 0; ACCEPT = 1; Loading @@ -68,5 +96,7 @@ message Telecom { // The list of active calls. repeated Call calls = 1; // The list of requested calls or call changes. repeated Call requests = 2; repeated Request requests = 2; // The list of call facilitators that this device currently supports. repeated CallFacilitator facilitators = 3; }
services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java +129 −73 Original line number Diff line number Diff line Loading @@ -16,18 +16,21 @@ package com.android.server.companion.datatransfer.contextsync; import android.content.ComponentName; import android.media.AudioManager; import android.os.Bundle; import android.telecom.Call; import android.telecom.Connection; import android.telecom.ConnectionRequest; import android.telecom.ConnectionService; import android.telecom.DisconnectCause; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.companion.CompanionDeviceManagerServiceInternal; import java.util.HashMap; import java.util.Map; Loading @@ -39,10 +42,47 @@ public class CallMetadataSyncConnectionService extends ConnectionService { private static final String TAG = "CallMetadataSyncConnectionService"; private AudioManager mAudioManager; private TelecomManager mTelecomManager; private final Map<PhoneAccountHandleIdentifier, PhoneAccountHandle> mPhoneAccountHandles = new HashMap<>(); @VisibleForTesting AudioManager mAudioManager; @VisibleForTesting TelecomManager mTelecomManager; private CompanionDeviceManagerServiceInternal mCdmsi; @VisibleForTesting final Map<CallMetadataSyncConnectionIdentifier, CallMetadataSyncConnection> mActiveConnections = new HashMap<>(); @VisibleForTesting final CrossDeviceSyncControllerCallback mCrossDeviceSyncControllerCallback = new CrossDeviceSyncControllerCallback() { @Override void processContextSyncMessage(int associationId, CallMetadataSyncData callMetadataSyncData) { // Add new calls or update existing calls. for (CallMetadataSyncData.Call call : callMetadataSyncData.getCalls()) { final CallMetadataSyncConnection existingConnection = mActiveConnections.get(new CallMetadataSyncConnectionIdentifier( associationId, call.getId())); if (existingConnection == null) { final Bundle extras = new Bundle(); extras.putInt(CrossDeviceSyncController.EXTRA_ASSOCIATION_ID, associationId); extras.putParcelable(CrossDeviceSyncController.EXTRA_CALL, call); mTelecomManager.addNewIncomingCall(call.getPhoneAccountHandle(), extras); } else { existingConnection.update(call); } } // Remove obsolete calls. mActiveConnections.values().removeIf(connection -> { if (!callMetadataSyncData.hasCall(connection.getCallId())) { connection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE)); return true; } return false; }); } }; @Override public void onCreate() { Loading @@ -50,83 +90,96 @@ public class CallMetadataSyncConnectionService extends ConnectionService { mAudioManager = getSystemService(AudioManager.class); mTelecomManager = getSystemService(TelecomManager.class); mCdmsi = LocalServices.getService(CompanionDeviceManagerServiceInternal.class); mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback); } /** * Registers a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced * device. */ private void registerPhoneAccount(int associationId, String appIdentifier, String humanReadableAppName) { final PhoneAccountHandleIdentifier phoneAccountHandleIdentifier = new PhoneAccountHandleIdentifier(associationId, appIdentifier); final PhoneAccount phoneAccount = createPhoneAccount(phoneAccountHandleIdentifier, humanReadableAppName); mTelecomManager.registerPhoneAccount(phoneAccount); mTelecomManager.enablePhoneAccount(mPhoneAccountHandles.get(phoneAccountHandleIdentifier), true); } /** * Unregisters a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced * device. */ private void unregisterPhoneAccount(int associationId, String appIdentifier) { mTelecomManager.unregisterPhoneAccount(mPhoneAccountHandles.remove( new PhoneAccountHandleIdentifier(associationId, appIdentifier))); @Override public Connection onCreateIncomingConnection(PhoneAccountHandle phoneAccountHandle, ConnectionRequest connectionRequest) { final int associationId = connectionRequest.getExtras().getInt( CrossDeviceSyncController.EXTRA_ASSOCIATION_ID); final CallMetadataSyncData.Call call = connectionRequest.getExtras().getParcelable( CrossDeviceSyncController.EXTRA_CALL, CallMetadataSyncData.Call.class); final CallMetadataSyncConnection connection = new CallMetadataSyncConnection( mTelecomManager, mAudioManager, associationId, call, new CallMetadataSyncConnectionCallback() { @Override void sendCallAction(int associationId, String callId, int action) { mCdmsi.sendCrossDeviceSyncMessage(associationId, CrossDeviceSyncController.createCallControlMessage(callId, action)); } }); connection.setConnectionProperties( Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED); return connection; } @VisibleForTesting PhoneAccount createPhoneAccount(PhoneAccountHandleIdentifier phoneAccountHandleIdentifier, String humanReadableAppName) { if (mPhoneAccountHandles.containsKey(phoneAccountHandleIdentifier)) { // Already exists! return null; } final PhoneAccountHandle handle = new PhoneAccountHandle( new ComponentName(this, CallMetadataSyncConnectionService.class), UUID.randomUUID().toString()); mPhoneAccountHandles.put(phoneAccountHandleIdentifier, handle); return new PhoneAccount.Builder(handle, humanReadableAppName) .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER | PhoneAccount.CAPABILITY_SELF_MANAGED).build(); } static final class PhoneAccountHandleIdentifier { private final int mAssociationId; private final String mAppIdentifier; PhoneAccountHandleIdentifier(int associationId, String appIdentifier) { mAssociationId = associationId; mAppIdentifier = appIdentifier; @Override public void onCreateIncomingConnectionFailed(PhoneAccountHandle phoneAccountHandle, ConnectionRequest connectionRequest) { Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId()); } public int getAssociationId() { return mAssociationId; @Override public Connection onCreateOutgoingConnection(PhoneAccountHandle phoneAccountHandle, ConnectionRequest connectionRequest) { final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle); final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId(UUID.randomUUID().toString()); call.setStatus(android.companion.Telecom.Call.UNKNOWN_STATUS); call.setPhoneAccountHandle(phoneAccountHandle); final CallMetadataSyncData.CallFacilitator callFacilitator = new CallMetadataSyncData.CallFacilitator(phoneAccount.getLabel().toString(), phoneAccount.getExtras().getString( CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID)); call.setFacilitator(callFacilitator); final int associationId = connectionRequest.getExtras().getInt( CrossDeviceSyncController.EXTRA_ASSOCIATION_ID); final CallMetadataSyncConnection connection = new CallMetadataSyncConnection( mTelecomManager, mAudioManager, associationId, call, new CallMetadataSyncConnectionCallback() { @Override void sendCallAction(int associationId, String callId, int action) { mCdmsi.sendCrossDeviceSyncMessage(associationId, CrossDeviceSyncController.createCallControlMessage(callId, action)); } }); connection.setConnectionProperties( Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED); public String getAppIdentifier() { return mAppIdentifier; mCdmsi.sendCrossDeviceSyncMessage(associationId, CrossDeviceSyncController.createCallCreateMessage(call.getId(), connectionRequest.getAddress().toString(), call.getFacilitator().getIdentifier())); return connection; } @Override public int hashCode() { return Objects.hash(mAssociationId, mAppIdentifier); public void onCreateOutgoingConnectionFailed(PhoneAccountHandle phoneAccountHandle, ConnectionRequest connectionRequest) { Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId()); } @Override public boolean equals(Object other) { if (other instanceof PhoneAccountHandleIdentifier) { return ((PhoneAccountHandleIdentifier) other).getAssociationId() == mAssociationId && mAppIdentifier != null && mAppIdentifier.equals( ((PhoneAccountHandleIdentifier) other).getAppIdentifier()); } return false; public void onCreateConnectionComplete(Connection connection) { if (connection instanceof CallMetadataSyncConnection) { ((CallMetadataSyncConnection) connection).initialize(); } } private static final class CallMetadataSyncConnectionIdentifier { @VisibleForTesting static final class CallMetadataSyncConnectionIdentifier { private final int mAssociationId; private final String mCallId; Loading @@ -153,18 +206,21 @@ public class CallMetadataSyncConnectionService extends ConnectionService { if (other instanceof CallMetadataSyncConnectionIdentifier) { return ((CallMetadataSyncConnectionIdentifier) other).getAssociationId() == mAssociationId && (((CallMetadataSyncConnectionIdentifier) other).getCallId() == mCallId); && mCallId != null && mCallId.equals( ((CallMetadataSyncConnectionIdentifier) other).getCallId()); } return false; } } private abstract static class CallMetadataSyncConnectionCallback { @VisibleForTesting abstract static class CallMetadataSyncConnectionCallback { abstract void sendCallAction(int associationId, String callId, int action); } private static class CallMetadataSyncConnection extends Connection { @VisibleForTesting static class CallMetadataSyncConnection extends Connection { private final TelecomManager mTelecomManager; private final AudioManager mAudioManager; Loading
services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java +150 −19 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.companion.ContextSyncMessage; import android.os.Parcel; import android.os.Parcelable; import android.telecom.PhoneAccountHandle; import java.util.ArrayList; import java.util.Collection; Loading @@ -34,7 +35,9 @@ import java.util.Set; class CallMetadataSyncData { final Map<String, CallMetadataSyncData.Call> mCalls = new HashMap<>(); final List<CallMetadataSyncData.Call> mRequests = new ArrayList<>(); final List<CallMetadataSyncData.CallCreateRequest> mCallCreateRequests = new ArrayList<>(); final List<CallMetadataSyncData.CallControlRequest> mCallControlRequests = new ArrayList<>(); final List<CallMetadataSyncData.CallFacilitator> mCallFacilitators = new ArrayList<>(); public void addCall(CallMetadataSyncData.Call call) { mCalls.put(call.getId(), call); Loading @@ -48,20 +51,145 @@ class CallMetadataSyncData { return mCalls.values(); } public void addRequest(CallMetadataSyncData.Call call) { mRequests.add(call); public void addCallCreateRequest(CallMetadataSyncData.CallCreateRequest request) { mCallCreateRequests.add(request); } public List<CallMetadataSyncData.Call> getRequests() { return mRequests; public List<CallMetadataSyncData.CallCreateRequest> getCallCreateRequests() { return mCallCreateRequests; } public void addCallControlRequest(CallMetadataSyncData.CallControlRequest request) { mCallControlRequests.add(request); } public List<CallMetadataSyncData.CallControlRequest> getCallControlRequests() { return mCallControlRequests; } public void addFacilitator(CallFacilitator facilitator) { mCallFacilitators.add(facilitator); } public List<CallFacilitator> getFacilitators() { return mCallFacilitators; } public static class CallFacilitator implements Parcelable { private String mName; private String mIdentifier; CallFacilitator() {} CallFacilitator(String name, String identifier) { mName = name; mIdentifier = identifier; } CallFacilitator(Parcel parcel) { this(parcel.readString(), parcel.readString()); } @Override public void writeToParcel(Parcel parcel, int parcelableFlags) { parcel.writeString(mName); parcel.writeString(mIdentifier); } public String getName() { return mName; } public String getIdentifier() { return mIdentifier; } public void setName(String name) { mName = name; } public void setIdentifier(String identifier) { mIdentifier = identifier; } @Override public int describeContents() { return 0; } @NonNull public static final Parcelable.Creator<CallFacilitator> CREATOR = new Parcelable.Creator<>() { @Override public CallFacilitator createFromParcel(Parcel source) { return new CallFacilitator(source); } @Override public CallFacilitator[] newArray(int size) { return new CallFacilitator[size]; } }; } public static class CallControlRequest { private String mId; private int mControl; public void setId(String id) { mId = id; } public void setControl(int control) { mControl = control; } public String getId() { return mId; } public int getControl() { return mControl; } } public static class CallCreateRequest { private String mId; private String mAddress; private CallFacilitator mFacilitator; public void setId(String id) { mId = id; } public void setAddress(String address) { mAddress = address; } public void setFacilitator(CallFacilitator facilitator) { mFacilitator = facilitator; } public String getId() { return mId; } public String getAddress() { return mAddress; } public CallFacilitator getFacilitator() { return mFacilitator; } } public static class Call implements Parcelable { private String mId; private String mCallerId; private byte[] mAppIcon; private String mAppName; private String mAppIdentifier; private CallFacilitator mFacilitator; private PhoneAccountHandle mPhoneAccountHandle; private int mStatus; private final Set<Integer> mControls = new HashSet<>(); Loading @@ -70,8 +198,11 @@ class CallMetadataSyncData { call.setId(parcel.readString()); call.setCallerId(parcel.readString()); call.setAppIcon(parcel.readBlob()); call.setAppName(parcel.readString()); call.setAppIdentifier(parcel.readString()); call.setFacilitator(parcel.readParcelable(CallFacilitator.class.getClassLoader(), CallFacilitator.class)); call.setPhoneAccountHandle( parcel.readParcelable(PhoneAccountHandle.class.getClassLoader(), android.telecom.PhoneAccountHandle.class)); call.setStatus(parcel.readInt()); final int numberOfControls = parcel.readInt(); for (int i = 0; i < numberOfControls; i++) { Loading @@ -85,8 +216,8 @@ class CallMetadataSyncData { parcel.writeString(mId); parcel.writeString(mCallerId); parcel.writeBlob(mAppIcon); parcel.writeString(mAppName); parcel.writeString(mAppIdentifier); parcel.writeParcelable(mFacilitator, parcelableFlags); parcel.writeParcelable(mPhoneAccountHandle, parcelableFlags); parcel.writeInt(mStatus); parcel.writeInt(mControls.size()); for (int control : mControls) { Loading @@ -106,12 +237,12 @@ class CallMetadataSyncData { mAppIcon = appIcon; } void setAppName(String appName) { mAppName = appName; void setFacilitator(CallFacilitator facilitator) { mFacilitator = facilitator; } void setAppIdentifier(String appIdentifier) { mAppIdentifier = appIdentifier; void setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) { mPhoneAccountHandle = phoneAccountHandle; } void setStatus(int status) { Loading @@ -134,12 +265,12 @@ class CallMetadataSyncData { return mAppIcon; } String getAppName() { return mAppName; CallFacilitator getFacilitator() { return mFacilitator; } String getAppIdentifier() { return mAppIdentifier; PhoneAccountHandle getPhoneAccountHandle() { return mPhoneAccountHandle; } int getStatus() { Loading
services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java +4 −9 Original line number Diff line number Diff line Loading @@ -66,16 +66,11 @@ public class CallMetadataSyncInCallService extends InCallService { @Override void processContextSyncMessage(int associationId, CallMetadataSyncData callMetadataSyncData) { final Iterator<CallMetadataSyncData.Call> iterator = callMetadataSyncData.getRequests().iterator(); final Iterator<CallMetadataSyncData.CallControlRequest> iterator = callMetadataSyncData.getCallControlRequests().iterator(); while (iterator.hasNext()) { final CallMetadataSyncData.Call call = iterator.next(); if (call.getId() != null) { // The call is already assigned an id; treat as control invocations. for (int control : call.getControls()) { processCallControlAction(call.getId(), control); } } final CallMetadataSyncData.CallControlRequest request = iterator.next(); processCallControlAction(request.getId(), request.getControl()); iterator.remove(); } } Loading
services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java +319 −13 File changed.Preview size limit exceeded, changes collapsed. Show changes