summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Christine Franks <christyfranks@google.com> 2023-04-21 02:16:00 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-04-21 02:16:00 +0000
commit0dd7f1f2372f1e003808f23031ed1351e53cbb9d (patch)
treedd242f5c2fe2f667b7044ca09d78ae44818d7a2c
parentd57e2fd6df3fe7afbb5ce746fca1b237c13b0600 (diff)
parente59f48655587bf9da5b4dbf2ea146da6d3abab1c (diff)
Merge "Process synced messages from secure channel" into udc-dev
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java279
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java12
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();
}