diff options
| author | 2023-04-21 02:16:00 +0000 | |
|---|---|---|
| committer | 2023-04-21 02:16:00 +0000 | |
| commit | 0dd7f1f2372f1e003808f23031ed1351e53cbb9d (patch) | |
| tree | dd242f5c2fe2f667b7044ca09d78ae44818d7a2c | |
| parent | d57e2fd6df3fe7afbb5ce746fca1b237c13b0600 (diff) | |
| parent | e59f48655587bf9da5b4dbf2ea146da6d3abab1c (diff) | |
Merge "Process synced messages from secure channel" into udc-dev
2 files changed, 276 insertions, 15 deletions
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java index 19d8b8783d81..6f99d8677646 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java @@ -17,26 +17,38 @@ 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.ConnectionService; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.UUID; /** Service for Telecom to bind to when call metadata is synced between devices. */ public class CallMetadataSyncConnectionService extends ConnectionService { + private static final String TAG = "CallMetadataSyncConnectionService"; + + private AudioManager mAudioManager; private TelecomManager mTelecomManager; - private final Map<String, PhoneAccountHandle> mPhoneAccountHandles = new HashMap<>(); + private final Map<PhoneAccountHandleIdentifier, PhoneAccountHandle> mPhoneAccountHandles = + new HashMap<>(); @Override public void onCreate() { super.onCreate(); + + mAudioManager = getSystemService(AudioManager.class); mTelecomManager = getSystemService(TelecomManager.class); } @@ -44,34 +56,277 @@ public class CallMetadataSyncConnectionService extends ConnectionService { * Registers a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced * device. */ - public void registerPhoneAccount(String packageName, String humanReadableAppName) { - final PhoneAccount phoneAccount = createPhoneAccount(packageName, humanReadableAppName); - if (phoneAccount != null) { - mTelecomManager.registerPhoneAccount(phoneAccount); - mTelecomManager.enablePhoneAccount(mPhoneAccountHandles.get(packageName), true); - } + 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. */ - public void unregisterPhoneAccount(String packageName) { - mTelecomManager.unregisterPhoneAccount(mPhoneAccountHandles.remove(packageName)); + private void unregisterPhoneAccount(int associationId, String appIdentifier) { + mTelecomManager.unregisterPhoneAccount(mPhoneAccountHandles.remove( + new PhoneAccountHandleIdentifier(associationId, appIdentifier))); } @VisibleForTesting - PhoneAccount createPhoneAccount(String packageName, String humanReadableAppName) { - if (mPhoneAccountHandles.containsKey(packageName)) { + 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(packageName, handle); + 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; + } + + public int getAssociationId() { + return mAssociationId; + } + + public String getAppIdentifier() { + return mAppIdentifier; + } + + @Override + public int hashCode() { + return Objects.hash(mAssociationId, mAppIdentifier); + } + + @Override + public boolean equals(Object other) { + if (other instanceof PhoneAccountHandleIdentifier) { + return ((PhoneAccountHandleIdentifier) other).getAssociationId() == mAssociationId + && mAppIdentifier != null + && mAppIdentifier.equals( + ((PhoneAccountHandleIdentifier) other).getAppIdentifier()); + } + return false; + } + } + + private static final class CallMetadataSyncConnectionIdentifier { + private final int mAssociationId; + private final long mCallId; + + CallMetadataSyncConnectionIdentifier(int associationId, long callId) { + mAssociationId = associationId; + mCallId = callId; + } + + public int getAssociationId() { + return mAssociationId; + } + + public long getCallId() { + return mCallId; + } + + @Override + public int hashCode() { + return Objects.hash(mAssociationId, mCallId); + } + + @Override + public boolean equals(Object other) { + if (other instanceof CallMetadataSyncConnectionIdentifier) { + return ((CallMetadataSyncConnectionIdentifier) other).getAssociationId() + == mAssociationId + && (((CallMetadataSyncConnectionIdentifier) other).getCallId() == mCallId); + } + return false; + } + } + + private abstract static class CallMetadataSyncConnectionCallback { + + abstract void sendCallAction(int associationId, long callId, int action); + + abstract void sendStateChange(int associationId, long callId, int newState); + } + + private static class CallMetadataSyncConnection extends Connection { + + private final TelecomManager mTelecomManager; + private final AudioManager mAudioManager; + private final int mAssociationId; + private final CallMetadataSyncData.Call mCall; + private final CallMetadataSyncConnectionCallback mCallback; + + CallMetadataSyncConnection(TelecomManager telecomManager, AudioManager audioManager, + int associationId, CallMetadataSyncData.Call call, + CallMetadataSyncConnectionCallback callback) { + mTelecomManager = telecomManager; + mAudioManager = audioManager; + mAssociationId = associationId; + mCall = call; + mCallback = callback; + } + + public long getCallId() { + return mCall.getId(); + } + + public void initialize() { + final int status = mCall.getStatus(); + if (status == android.companion.Telecom.Call.RINGING_SILENCED) { + mTelecomManager.silenceRinger(); + } + final int state = CrossDeviceCall.convertStatusToState(status); + if (state == Call.STATE_RINGING) { + setRinging(); + } else if (state == Call.STATE_ACTIVE) { + setActive(); + } else if (state == Call.STATE_HOLDING) { + setOnHold(); + } else { + Slog.e(TAG, "Could not initialize call to unknown state"); + } + + final Bundle extras = new Bundle(); + extras.putLong(CrossDeviceCall.EXTRA_CALL_ID, mCall.getId()); + putExtras(extras); + + int capabilities = getConnectionCapabilities(); + if (mCall.hasControl(android.companion.Telecom.Call.PUT_ON_HOLD)) { + capabilities |= CAPABILITY_HOLD; + } else { + capabilities &= ~CAPABILITY_HOLD; + } + if (mCall.hasControl(android.companion.Telecom.Call.MUTE)) { + capabilities |= CAPABILITY_MUTE; + } else { + capabilities &= ~CAPABILITY_MUTE; + } + mAudioManager.setMicrophoneMute( + mCall.hasControl(android.companion.Telecom.Call.UNMUTE)); + if (capabilities != getConnectionCapabilities()) { + setConnectionCapabilities(capabilities); + } + } + + public void update(CallMetadataSyncData.Call call) { + final int status = call.getStatus(); + if (status == android.companion.Telecom.Call.RINGING_SILENCED + && mCall.getStatus() != android.companion.Telecom.Call.RINGING_SILENCED) { + mTelecomManager.silenceRinger(); + } + mCall.setStatus(status); + final int state = CrossDeviceCall.convertStatusToState(status); + if (state != getState()) { + if (state == Call.STATE_RINGING) { + setRinging(); + } else if (state == Call.STATE_ACTIVE) { + setActive(); + } else if (state == Call.STATE_HOLDING) { + setOnHold(); + } else { + Slog.e(TAG, "Could not update call to unknown state"); + } + } + + int capabilities = getConnectionCapabilities(); + final boolean hasHoldControl = mCall.hasControl( + android.companion.Telecom.Call.PUT_ON_HOLD) + || mCall.hasControl(android.companion.Telecom.Call.TAKE_OFF_HOLD); + if (hasHoldControl != ((getConnectionCapabilities() & CAPABILITY_HOLD) + == CAPABILITY_HOLD)) { + if (hasHoldControl) { + capabilities |= CAPABILITY_HOLD; + } else { + capabilities &= ~CAPABILITY_HOLD; + } + } + final boolean hasMuteControl = mCall.hasControl(android.companion.Telecom.Call.MUTE); + if (hasMuteControl != ((getConnectionCapabilities() & CAPABILITY_MUTE) + == CAPABILITY_MUTE)) { + if (hasMuteControl) { + capabilities |= CAPABILITY_MUTE; + } else { + capabilities &= ~CAPABILITY_MUTE; + } + } + mAudioManager.setMicrophoneMute( + mCall.hasControl(android.companion.Telecom.Call.UNMUTE)); + if (capabilities != getConnectionCapabilities()) { + setConnectionCapabilities(capabilities); + } + } + + @Override + public void onAnswer(int videoState) { + sendCallAction(android.companion.Telecom.Call.ACCEPT); + } + + @Override + public void onReject() { + sendCallAction(android.companion.Telecom.Call.REJECT); + } + + @Override + public void onReject(int rejectReason) { + onReject(); + } + + @Override + public void onReject(String replyMessage) { + onReject(); + } + + @Override + public void onSilence() { + sendCallAction(android.companion.Telecom.Call.SILENCE); + } + + @Override + public void onHold() { + sendCallAction(android.companion.Telecom.Call.PUT_ON_HOLD); + } + + @Override + public void onUnhold() { + sendCallAction(android.companion.Telecom.Call.TAKE_OFF_HOLD); + } + + @Override + public void onMuteStateChanged(boolean isMuted) { + sendCallAction(isMuted ? android.companion.Telecom.Call.MUTE + : android.companion.Telecom.Call.UNMUTE); + } + + @Override + public void onDisconnect() { + sendCallAction(android.companion.Telecom.Call.END); + } + + @Override + public void onStateChanged(int state) { + mCallback.sendStateChange(mAssociationId, mCall.getId(), state); + } + + private void sendCallAction(int action) { + mCallback.sendCallAction(mAssociationId, mCall.getId(), action); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java index 3ed95eb3c878..bacf2568d9ca 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java @@ -45,16 +45,22 @@ public class CallMetadataSyncConnectionServiceTest { @Test public void createPhoneAccount_success() { final PhoneAccount phoneAccount = mSyncConnectionService.createPhoneAccount( - "com.google.test", "Test App"); + new CallMetadataSyncConnectionService.PhoneAccountHandleIdentifier(/* + associationId= */ + 0, "com.google.test"), "Test App"); assertWithMessage("Could not create phone account").that(phoneAccount).isNotNull(); } @Test public void createPhoneAccount_alreadyExists_doesNotCreateAnother() { final PhoneAccount phoneAccount = mSyncConnectionService.createPhoneAccount( - "com.google.test", "Test App"); + new CallMetadataSyncConnectionService.PhoneAccountHandleIdentifier(/* + associationId= */ + 0, "com.google.test"), "Test App"); final PhoneAccount phoneAccount2 = mSyncConnectionService.createPhoneAccount( - "com.google.test", "Test App #2"); + new CallMetadataSyncConnectionService.PhoneAccountHandleIdentifier(/* + associationId= */ + 0, "com.google.test"), "Test App #2"); assertWithMessage("Could not create phone account").that(phoneAccount).isNotNull(); assertWithMessage("Unexpectedly created second phone account").that(phoneAccount2).isNull(); } |