Merge changes I02fb643b,If62d66bc into udc-dev
* changes: Sync facilitators on transport ready Small context sync telecom bugfixes
This commit is contained in:
committed by
Android (Google) Code Review
commit
b9de8fb70d
@@ -42,6 +42,9 @@ message Telecom {
|
||||
ONGOING = 2;
|
||||
ON_HOLD = 3;
|
||||
RINGING_SILENCED = 4;
|
||||
AUDIO_PROCESSING = 5;
|
||||
RINGING_SIMULATED = 6;
|
||||
DISCONNECTED = 7;
|
||||
}
|
||||
Status status = 3;
|
||||
|
||||
@@ -89,8 +92,6 @@ message Telecom {
|
||||
END = 6;
|
||||
PUT_ON_HOLD = 7;
|
||||
TAKE_OFF_HOLD = 8;
|
||||
REJECT_AND_BLOCK = 9;
|
||||
IGNORE = 10;
|
||||
}
|
||||
|
||||
// The list of active calls.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.companion;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
/**
|
||||
@@ -34,7 +35,12 @@ public class CompanionDeviceConfig {
|
||||
* Returns whether the given flag is currently enabled, with a default value of {@code false}.
|
||||
*/
|
||||
public static boolean isEnabled(String flag) {
|
||||
return DeviceConfig.getBoolean(NAMESPACE_COMPANION, flag, /* defaultValue= */ false);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return DeviceConfig.getBoolean(NAMESPACE_COMPANION, flag, /* defaultValue= */ false);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1383,10 +1383,11 @@ public class CompanionDeviceManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback) {
|
||||
public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback,
|
||||
@CrossDeviceSyncControllerCallback.Type int type) {
|
||||
if (CompanionDeviceConfig.isEnabled(
|
||||
CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
|
||||
mCrossDeviceSyncController.registerCallMetadataSyncCallback(callback);
|
||||
mCrossDeviceSyncController.registerCallMetadataSyncCallback(callback, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ public interface CompanionDeviceManagerServiceInternal {
|
||||
* Registers a callback from an InCallService / ConnectionService to CDM to process sync
|
||||
* requests and perform call control actions.
|
||||
*/
|
||||
void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback);
|
||||
void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback,
|
||||
@CrossDeviceSyncControllerCallback.Type int type);
|
||||
|
||||
/**
|
||||
* Requests a sync from an InCallService / ConnectionService to CDM, for the given association
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.companion.datatransfer.contextsync;
|
||||
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.telecom.Call;
|
||||
import android.telecom.Connection;
|
||||
@@ -62,20 +63,14 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
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 {
|
||||
if (existingConnection != null) {
|
||||
existingConnection.update(call);
|
||||
}
|
||||
}
|
||||
// Remove obsolete calls.
|
||||
mActiveConnections.values().removeIf(connection -> {
|
||||
if (!callMetadataSyncData.hasCall(connection.getCallId())) {
|
||||
if (associationId == connection.getAssociationId()
|
||||
&& !callMetadataSyncData.hasCall(connection.getCallId())) {
|
||||
connection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
|
||||
return true;
|
||||
}
|
||||
@@ -91,7 +86,8 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
mAudioManager = getSystemService(AudioManager.class);
|
||||
mTelecomManager = getSystemService(TelecomManager.class);
|
||||
mCdmsi = LocalServices.getService(CompanionDeviceManagerServiceInternal.class);
|
||||
mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback);
|
||||
mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback,
|
||||
CrossDeviceSyncControllerCallback.TYPE_CONNECTION_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,6 +97,11 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);
|
||||
final CallMetadataSyncData.Call call = connectionRequest.getExtras().getParcelable(
|
||||
CrossDeviceSyncController.EXTRA_CALL, CallMetadataSyncData.Call.class);
|
||||
// InCallServices outside of framework (like Dialer's) might try to read this, and crash
|
||||
// when they can't. Remove it once we're done with it, as well as the other internal ones.
|
||||
connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL);
|
||||
connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID);
|
||||
connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);
|
||||
final CallMetadataSyncConnection connection = new CallMetadataSyncConnection(
|
||||
mTelecomManager,
|
||||
mAudioManager,
|
||||
@@ -113,15 +114,17 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
CrossDeviceSyncController.createCallControlMessage(callId, action));
|
||||
}
|
||||
});
|
||||
connection.setConnectionProperties(
|
||||
Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED);
|
||||
connection.setConnectionProperties(Connection.PROPERTY_IS_EXTERNAL_CALL);
|
||||
connection.setInitializing();
|
||||
return connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateIncomingConnectionFailed(PhoneAccountHandle phoneAccountHandle,
|
||||
ConnectionRequest connectionRequest) {
|
||||
Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId());
|
||||
final String id =
|
||||
phoneAccountHandle != null ? phoneAccountHandle.getId() : "unknown PhoneAccount";
|
||||
Slog.e(TAG, "onCreateOutgoingConnectionFailed for: " + id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,7 +135,6 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
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(
|
||||
@@ -142,6 +144,10 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
final int associationId = connectionRequest.getExtras().getInt(
|
||||
CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);
|
||||
|
||||
connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL);
|
||||
connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID);
|
||||
connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);
|
||||
|
||||
final CallMetadataSyncConnection connection = new CallMetadataSyncConnection(
|
||||
mTelecomManager,
|
||||
mAudioManager,
|
||||
@@ -154,8 +160,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
CrossDeviceSyncController.createCallControlMessage(callId, action));
|
||||
}
|
||||
});
|
||||
connection.setConnectionProperties(
|
||||
Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED);
|
||||
connection.setConnectionProperties(Connection.PROPERTY_IS_EXTERNAL_CALL);
|
||||
|
||||
mCdmsi.sendCrossDeviceSyncMessage(associationId,
|
||||
CrossDeviceSyncController.createCallCreateMessage(call.getId(),
|
||||
@@ -168,13 +173,21 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
@Override
|
||||
public void onCreateOutgoingConnectionFailed(PhoneAccountHandle phoneAccountHandle,
|
||||
ConnectionRequest connectionRequest) {
|
||||
Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId());
|
||||
final String id =
|
||||
phoneAccountHandle != null ? phoneAccountHandle.getId() : "unknown PhoneAccount";
|
||||
Slog.e(TAG, "onCreateOutgoingConnectionFailed for: " + id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateConnectionComplete(Connection connection) {
|
||||
if (connection instanceof CallMetadataSyncConnection) {
|
||||
((CallMetadataSyncConnection) connection).initialize();
|
||||
final CallMetadataSyncConnection callMetadataSyncConnection =
|
||||
(CallMetadataSyncConnection) connection;
|
||||
callMetadataSyncConnection.initialize();
|
||||
mActiveConnections.put(new CallMetadataSyncConnectionIdentifier(
|
||||
callMetadataSyncConnection.getAssociationId(),
|
||||
callMetadataSyncConnection.getCallId()),
|
||||
callMetadataSyncConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +255,11 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
return mCall.getId();
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
public int getAssociationId() {
|
||||
return mAssociationId;
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
final int status = mCall.getStatus();
|
||||
if (status == android.companion.Telecom.Call.RINGING_SILENCED) {
|
||||
mTelecomManager.silenceRinger();
|
||||
@@ -254,12 +271,21 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
setActive();
|
||||
} else if (state == Call.STATE_HOLDING) {
|
||||
setOnHold();
|
||||
} else if (state == Call.STATE_DISCONNECTED) {
|
||||
setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
|
||||
} else {
|
||||
Slog.e(TAG, "Could not initialize call to unknown state");
|
||||
setInitialized();
|
||||
}
|
||||
|
||||
final String callerId = mCall.getCallerId();
|
||||
if (callerId != null) {
|
||||
setCallerDisplayName(callerId, TelecomManager.PRESENTATION_ALLOWED);
|
||||
setAddress(Uri.fromParts("custom", mCall.getCallerId(), null),
|
||||
TelecomManager.PRESENTATION_ALLOWED);
|
||||
}
|
||||
|
||||
final Bundle extras = new Bundle();
|
||||
extras.putString(CrossDeviceCall.EXTRA_CALL_ID, mCall.getId());
|
||||
extras.putString(CrossDeviceSyncController.EXTRA_CALL_ID, mCall.getId());
|
||||
putExtras(extras);
|
||||
|
||||
int capabilities = getConnectionCapabilities();
|
||||
@@ -280,7 +306,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
}
|
||||
}
|
||||
|
||||
public void update(CallMetadataSyncData.Call call) {
|
||||
private 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) {
|
||||
@@ -295,31 +321,29 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
|
||||
setActive();
|
||||
} else if (state == Call.STATE_HOLDING) {
|
||||
setOnHold();
|
||||
} else if (state == Call.STATE_DISCONNECTED) {
|
||||
setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
|
||||
} else {
|
||||
Slog.e(TAG, "Could not update call to unknown state");
|
||||
}
|
||||
}
|
||||
|
||||
int capabilities = getConnectionCapabilities();
|
||||
mCall.setControls(call.getControls());
|
||||
final boolean hasHoldControl = mCall.hasControl(
|
||||
android.companion.Telecom.PUT_ON_HOLD)
|
||||
|| mCall.hasControl(android.companion.Telecom.TAKE_OFF_HOLD);
|
||||
if (hasHoldControl != ((getConnectionCapabilities() & CAPABILITY_HOLD)
|
||||
== CAPABILITY_HOLD)) {
|
||||
if (hasHoldControl) {
|
||||
capabilities |= CAPABILITY_HOLD;
|
||||
} else {
|
||||
capabilities &= ~CAPABILITY_HOLD;
|
||||
}
|
||||
if (hasHoldControl) {
|
||||
capabilities |= CAPABILITY_HOLD;
|
||||
} else {
|
||||
capabilities &= ~CAPABILITY_HOLD;
|
||||
}
|
||||
final boolean hasMuteControl = mCall.hasControl(android.companion.Telecom.MUTE);
|
||||
if (hasMuteControl != ((getConnectionCapabilities() & CAPABILITY_MUTE)
|
||||
== CAPABILITY_MUTE)) {
|
||||
if (hasMuteControl) {
|
||||
capabilities |= CAPABILITY_MUTE;
|
||||
} else {
|
||||
capabilities &= ~CAPABILITY_MUTE;
|
||||
}
|
||||
final boolean hasMuteControl = mCall.hasControl(android.companion.Telecom.MUTE)
|
||||
|| mCall.hasControl(android.companion.Telecom.UNMUTE);
|
||||
if (hasMuteControl) {
|
||||
capabilities |= CAPABILITY_MUTE;
|
||||
} else {
|
||||
capabilities &= ~CAPABILITY_MUTE;
|
||||
}
|
||||
mAudioManager.setMicrophoneMute(
|
||||
mCall.hasControl(android.companion.Telecom.UNMUTE));
|
||||
|
||||
@@ -20,7 +20,6 @@ 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;
|
||||
@@ -189,7 +188,6 @@ class CallMetadataSyncData {
|
||||
private String mCallerId;
|
||||
private byte[] mAppIcon;
|
||||
private CallFacilitator mFacilitator;
|
||||
private PhoneAccountHandle mPhoneAccountHandle;
|
||||
private int mStatus;
|
||||
private final Set<Integer> mControls = new HashSet<>();
|
||||
|
||||
@@ -200,9 +198,6 @@ class CallMetadataSyncData {
|
||||
call.setAppIcon(parcel.readBlob());
|
||||
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++) {
|
||||
@@ -217,7 +212,6 @@ class CallMetadataSyncData {
|
||||
parcel.writeString(mCallerId);
|
||||
parcel.writeBlob(mAppIcon);
|
||||
parcel.writeParcelable(mFacilitator, parcelableFlags);
|
||||
parcel.writeParcelable(mPhoneAccountHandle, parcelableFlags);
|
||||
parcel.writeInt(mStatus);
|
||||
parcel.writeInt(mControls.size());
|
||||
for (int control : mControls) {
|
||||
@@ -241,10 +235,6 @@ class CallMetadataSyncData {
|
||||
mFacilitator = facilitator;
|
||||
}
|
||||
|
||||
void setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
|
||||
mPhoneAccountHandle = phoneAccountHandle;
|
||||
}
|
||||
|
||||
void setStatus(int status) {
|
||||
mStatus = status;
|
||||
}
|
||||
@@ -253,6 +243,11 @@ class CallMetadataSyncData {
|
||||
mControls.add(control);
|
||||
}
|
||||
|
||||
void setControls(Set<Integer> controls) {
|
||||
mControls.clear();
|
||||
mControls.addAll(controls);
|
||||
}
|
||||
|
||||
String getId() {
|
||||
return mId;
|
||||
}
|
||||
@@ -269,10 +264,6 @@ class CallMetadataSyncData {
|
||||
return mFacilitator;
|
||||
}
|
||||
|
||||
PhoneAccountHandle getPhoneAccountHandle() {
|
||||
return mPhoneAccountHandle;
|
||||
}
|
||||
|
||||
int getStatus() {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
@@ -79,16 +79,15 @@ public class CallMetadataSyncInCallService extends InCallService {
|
||||
int callControlAction) {
|
||||
final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId,
|
||||
mCurrentCalls.values());
|
||||
if (crossDeviceCall == null) {
|
||||
return;
|
||||
}
|
||||
switch (callControlAction) {
|
||||
case android.companion.Telecom.ACCEPT:
|
||||
if (crossDeviceCall != null) {
|
||||
crossDeviceCall.doAccept();
|
||||
}
|
||||
crossDeviceCall.doAccept();
|
||||
break;
|
||||
case android.companion.Telecom.REJECT:
|
||||
if (crossDeviceCall != null) {
|
||||
crossDeviceCall.doReject();
|
||||
}
|
||||
crossDeviceCall.doReject();
|
||||
break;
|
||||
case android.companion.Telecom.SILENCE:
|
||||
doSilence();
|
||||
@@ -100,19 +99,13 @@ public class CallMetadataSyncInCallService extends InCallService {
|
||||
doUnmute();
|
||||
break;
|
||||
case android.companion.Telecom.END:
|
||||
if (crossDeviceCall != null) {
|
||||
crossDeviceCall.doEnd();
|
||||
}
|
||||
crossDeviceCall.doEnd();
|
||||
break;
|
||||
case android.companion.Telecom.PUT_ON_HOLD:
|
||||
if (crossDeviceCall != null) {
|
||||
crossDeviceCall.doPutOnHold();
|
||||
}
|
||||
crossDeviceCall.doPutOnHold();
|
||||
break;
|
||||
case android.companion.Telecom.TAKE_OFF_HOLD:
|
||||
if (crossDeviceCall != null) {
|
||||
crossDeviceCall.doTakeOffHold();
|
||||
}
|
||||
crossDeviceCall.doTakeOffHold();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
@@ -148,7 +141,8 @@ public class CallMetadataSyncInCallService extends InCallService {
|
||||
super.onCreate();
|
||||
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
|
||||
mCdmsi = LocalServices.getService(CompanionDeviceManagerServiceInternal.class);
|
||||
mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback);
|
||||
mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback,
|
||||
CrossDeviceSyncControllerCallback.TYPE_IN_CALL_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +150,7 @@ public class CallMetadataSyncInCallService extends InCallService {
|
||||
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
|
||||
&& mNumberOfActiveSyncAssociations > 0) {
|
||||
mCurrentCalls.putAll(getCalls().stream().collect(Collectors.toMap(call -> call,
|
||||
call -> new CrossDeviceCall(getPackageManager(), call, getCallAudioState()))));
|
||||
call -> new CrossDeviceCall(this, call, getCallAudioState()))));
|
||||
mCurrentCalls.keySet().forEach(call -> call.registerCallback(mTelecomCallback,
|
||||
getMainThreadHandler()));
|
||||
sync(getUserId());
|
||||
@@ -182,7 +176,7 @@ public class CallMetadataSyncInCallService extends InCallService {
|
||||
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
|
||||
&& mNumberOfActiveSyncAssociations > 0) {
|
||||
mCurrentCalls.put(call,
|
||||
new CrossDeviceCall(getPackageManager(), call, getCallAudioState()));
|
||||
new CrossDeviceCall(this, call, getCallAudioState()));
|
||||
call.registerCallback(mTelecomCallback);
|
||||
sync(getUserId());
|
||||
}
|
||||
|
||||
@@ -17,10 +17,15 @@
|
||||
package com.android.server.companion.datatransfer.contextsync;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.telecom.Call;
|
||||
import android.telecom.CallAudioState;
|
||||
import android.telecom.TelecomManager;
|
||||
import android.telecom.VideoProfile;
|
||||
import android.util.Slog;
|
||||
|
||||
@@ -35,40 +40,51 @@ public class CrossDeviceCall {
|
||||
|
||||
private static final String TAG = "CrossDeviceCall";
|
||||
|
||||
public static final String EXTRA_CALL_ID =
|
||||
"com.android.companion.datatransfer.contextsync.extra.CALL_ID";
|
||||
|
||||
private final String mId;
|
||||
private Call mCall;
|
||||
private final Call mCall;
|
||||
@VisibleForTesting boolean mIsEnterprise;
|
||||
@VisibleForTesting boolean mIsOtt;
|
||||
private final String mCallingAppPackageName;
|
||||
private String mCallingAppName;
|
||||
private byte[] mCallingAppIcon;
|
||||
private String mCallerDisplayName;
|
||||
private int mCallerDisplayNamePresentation;
|
||||
private int mStatus = android.companion.Telecom.Call.UNKNOWN_STATUS;
|
||||
private String mContactDisplayName;
|
||||
private Uri mHandle;
|
||||
private int mHandlePresentation;
|
||||
private boolean mIsMuted;
|
||||
private final Set<Integer> mControls = new HashSet<>();
|
||||
private final boolean mIsCallPlacedByContextSync;
|
||||
|
||||
public CrossDeviceCall(PackageManager packageManager, @NonNull Call call,
|
||||
public CrossDeviceCall(Context context, @NonNull Call call,
|
||||
CallAudioState callAudioState) {
|
||||
this(packageManager, call.getDetails(), callAudioState);
|
||||
mCall = call;
|
||||
call.putExtra(EXTRA_CALL_ID, mId);
|
||||
this(context, call, call.getDetails(), callAudioState);
|
||||
}
|
||||
|
||||
CrossDeviceCall(PackageManager packageManager, Call.Details callDetails,
|
||||
CrossDeviceCall(Context context, Call.Details callDetails,
|
||||
CallAudioState callAudioState) {
|
||||
this(context, /* call= */ null, callDetails, callAudioState);
|
||||
}
|
||||
|
||||
private CrossDeviceCall(Context context, @Nullable Call call,
|
||||
Call.Details callDetails, CallAudioState callAudioState) {
|
||||
mCall = call;
|
||||
final String predefinedId = callDetails.getIntentExtras() != null
|
||||
? callDetails.getIntentExtras().getString(EXTRA_CALL_ID) : null;
|
||||
mId = predefinedId != null ? predefinedId : UUID.randomUUID().toString();
|
||||
? callDetails.getIntentExtras().getString(CrossDeviceSyncController.EXTRA_CALL_ID)
|
||||
: null;
|
||||
final String generatedId = UUID.randomUUID().toString();
|
||||
mId = predefinedId != null ? (generatedId + predefinedId) : generatedId;
|
||||
if (call != null) {
|
||||
call.putExtra(CrossDeviceSyncController.EXTRA_CALL_ID, mId);
|
||||
}
|
||||
mIsCallPlacedByContextSync =
|
||||
new ComponentName(context, CallMetadataSyncConnectionService.class)
|
||||
.equals(callDetails.getAccountHandle().getComponentName());
|
||||
mCallingAppPackageName =
|
||||
callDetails.getAccountHandle().getComponentName().getPackageName();
|
||||
mIsOtt = (callDetails.getCallCapabilities() & Call.Details.PROPERTY_SELF_MANAGED)
|
||||
== Call.Details.PROPERTY_SELF_MANAGED;
|
||||
mIsEnterprise = (callDetails.getCallProperties() & Call.Details.PROPERTY_ENTERPRISE_CALL)
|
||||
== Call.Details.PROPERTY_ENTERPRISE_CALL;
|
||||
final PackageManager packageManager = context.getPackageManager();
|
||||
try {
|
||||
final ApplicationInfo applicationInfo = packageManager
|
||||
.getApplicationInfo(mCallingAppPackageName,
|
||||
@@ -108,7 +124,10 @@ public class CrossDeviceCall {
|
||||
@VisibleForTesting
|
||||
void updateCallDetails(Call.Details callDetails) {
|
||||
mCallerDisplayName = callDetails.getCallerDisplayName();
|
||||
mCallerDisplayNamePresentation = callDetails.getCallerDisplayNamePresentation();
|
||||
mContactDisplayName = callDetails.getContactDisplayName();
|
||||
mHandle = callDetails.getHandle();
|
||||
mHandlePresentation = callDetails.getHandlePresentation();
|
||||
mStatus = convertStateToStatus(callDetails.getState());
|
||||
mControls.clear();
|
||||
if (mStatus == android.companion.Telecom.Call.RINGING
|
||||
@@ -145,7 +164,14 @@ public class CrossDeviceCall {
|
||||
return android.companion.Telecom.Call.ONGOING;
|
||||
case Call.STATE_RINGING:
|
||||
return android.companion.Telecom.Call.RINGING;
|
||||
case Call.STATE_AUDIO_PROCESSING:
|
||||
return android.companion.Telecom.Call.AUDIO_PROCESSING;
|
||||
case Call.STATE_SIMULATED_RINGING:
|
||||
return android.companion.Telecom.Call.RINGING_SIMULATED;
|
||||
case Call.STATE_DISCONNECTED:
|
||||
return android.companion.Telecom.Call.DISCONNECTED;
|
||||
default:
|
||||
Slog.e(TAG, "Couldn't resolve state to status: " + callState);
|
||||
return android.companion.Telecom.Call.UNKNOWN_STATUS;
|
||||
}
|
||||
}
|
||||
@@ -163,6 +189,12 @@ public class CrossDeviceCall {
|
||||
case android.companion.Telecom.Call.RINGING:
|
||||
case android.companion.Telecom.Call.RINGING_SILENCED:
|
||||
return Call.STATE_RINGING;
|
||||
case android.companion.Telecom.Call.AUDIO_PROCESSING:
|
||||
return Call.STATE_AUDIO_PROCESSING;
|
||||
case android.companion.Telecom.Call.RINGING_SIMULATED:
|
||||
return Call.STATE_SIMULATED_RINGING;
|
||||
case android.companion.Telecom.Call.DISCONNECTED:
|
||||
return Call.STATE_DISCONNECTED;
|
||||
case android.companion.Telecom.Call.UNKNOWN_STATUS:
|
||||
default:
|
||||
return Call.STATE_NEW;
|
||||
@@ -195,10 +227,23 @@ public class CrossDeviceCall {
|
||||
* @param isAdminBlocked whether there is an admin that has blocked contacts over Bluetooth
|
||||
*/
|
||||
public String getReadableCallerId(boolean isAdminBlocked) {
|
||||
if (mIsOtt) {
|
||||
if (mIsEnterprise && isAdminBlocked) {
|
||||
// Cannot use any contact information.
|
||||
return getNonContactString();
|
||||
}
|
||||
return mContactDisplayName != null ? mContactDisplayName : getNonContactString();
|
||||
}
|
||||
|
||||
private String getNonContactString() {
|
||||
if (mCallerDisplayName != null
|
||||
&& mCallerDisplayNamePresentation == TelecomManager.PRESENTATION_ALLOWED) {
|
||||
return mCallerDisplayName;
|
||||
}
|
||||
return mIsEnterprise && isAdminBlocked ? mCallerDisplayName : mContactDisplayName;
|
||||
if (mHandle != null && mHandle.getSchemeSpecificPart() != null
|
||||
&& mHandlePresentation == TelecomManager.PRESENTATION_ALLOWED) {
|
||||
return mHandle.getSchemeSpecificPart();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
@@ -209,6 +254,10 @@ public class CrossDeviceCall {
|
||||
return mControls;
|
||||
}
|
||||
|
||||
public boolean isCallPlacedByContextSync() {
|
||||
return mIsCallPlacedByContextSync;
|
||||
}
|
||||
|
||||
void doAccept() {
|
||||
mCall.answer(VideoProfile.STATE_AUDIO_ONLY);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_C
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.companion.AssociationInfo;
|
||||
import android.companion.CompanionDeviceManager;
|
||||
import android.companion.ContextSyncMessage;
|
||||
import android.companion.IOnMessageReceivedListener;
|
||||
import android.companion.IOnTransportsChangedListener;
|
||||
@@ -44,8 +45,10 @@ import com.android.server.companion.CompanionDeviceConfig;
|
||||
import com.android.server.companion.transport.CompanionTransportManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -54,6 +57,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Monitors connections and sending / receiving of synced data.
|
||||
@@ -62,6 +66,13 @@ public class CrossDeviceSyncController {
|
||||
|
||||
private static final String TAG = "CrossDeviceSyncController";
|
||||
|
||||
public static final String EXTRA_CALL_ID =
|
||||
"com.android.companion.datatransfer.contextsync.extra.CALL_ID";
|
||||
static final String EXTRA_FACILITATOR_ICON =
|
||||
"com.android.companion.datatransfer.contextsync.extra.FACILITATOR_ICON";
|
||||
static final String EXTRA_IS_REMOTE_ORIGIN =
|
||||
"com.android.companion.datatransfer.contextsync.extra.IS_REMOTE_ORIGIN";
|
||||
|
||||
static final String EXTRA_ASSOCIATION_ID =
|
||||
"com.android.server.companion.datatransfer.contextsync.extra.ASSOCIATION_ID";
|
||||
static final String EXTRA_CALL =
|
||||
@@ -78,11 +89,13 @@ public class CrossDeviceSyncController {
|
||||
private final Context mContext;
|
||||
private final CompanionTransportManager mCompanionTransportManager;
|
||||
private final PhoneAccountManager mPhoneAccountManager;
|
||||
private final CallManager mCallManager;
|
||||
private final List<AssociationInfo> mConnectedAssociations = new ArrayList<>();
|
||||
private final Set<Integer> mBlocklist = new HashSet<>();
|
||||
private final List<CallMetadataSyncData.CallFacilitator> mCallFacilitators = new ArrayList<>();
|
||||
|
||||
private CrossDeviceSyncControllerCallback mCrossDeviceSyncControllerCallback;
|
||||
private WeakReference<CrossDeviceSyncControllerCallback> mInCallServiceCallbackRef;
|
||||
private WeakReference<CrossDeviceSyncControllerCallback> mConnectionServiceCallbackRef;
|
||||
|
||||
public CrossDeviceSyncController(Context context,
|
||||
CompanionTransportManager companionTransportManager) {
|
||||
@@ -104,25 +117,77 @@ public class CrossDeviceSyncController {
|
||||
mConnectedAssociations);
|
||||
mConnectedAssociations.clear();
|
||||
mConnectedAssociations.addAll(newAssociations);
|
||||
if (mCrossDeviceSyncControllerCallback == null) {
|
||||
Slog.w(TAG, "No callback to report transports changed");
|
||||
return;
|
||||
}
|
||||
for (AssociationInfo associationInfo : newAssociations) {
|
||||
if (!existingAssociations.contains(associationInfo)
|
||||
&& !isAssociationBlocked(associationInfo.getId())) {
|
||||
mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
|
||||
associationInfo.getUserId(), /* added= */ true);
|
||||
mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
|
||||
if (!existingAssociations.contains(associationInfo)) {
|
||||
// New association.
|
||||
if (!isAssociationBlocked(associationInfo)) {
|
||||
final CrossDeviceSyncControllerCallback callback =
|
||||
mInCallServiceCallbackRef != null
|
||||
? mInCallServiceCallbackRef.get() : null;
|
||||
if (callback != null) {
|
||||
callback.updateNumberOfActiveSyncAssociations(
|
||||
associationInfo.getUserId(), /* added= */ true);
|
||||
callback.requestCrossDeviceSync(associationInfo);
|
||||
} else {
|
||||
Slog.w(TAG, "No callback to report new transport");
|
||||
syncMessageToDevice(associationInfo.getId(),
|
||||
createFacilitatorMessage());
|
||||
}
|
||||
} else {
|
||||
mBlocklist.add(associationInfo.getId());
|
||||
Slog.i(TAG, "New association was blocked from context syncing");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (AssociationInfo associationInfo : existingAssociations) {
|
||||
if (!newAssociations.contains(associationInfo)) {
|
||||
if (isAssociationBlocked(associationInfo.getId())) {
|
||||
mBlocklist.remove(associationInfo.getId());
|
||||
} else {
|
||||
mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
|
||||
associationInfo.getUserId(), /* added= */ false);
|
||||
// Removed association!
|
||||
mBlocklist.remove(associationInfo.getId());
|
||||
if (!isAssociationBlockedLocal(associationInfo.getId())) {
|
||||
final CrossDeviceSyncControllerCallback callback =
|
||||
mInCallServiceCallbackRef != null
|
||||
? mInCallServiceCallbackRef.get() : null;
|
||||
if (callback != null) {
|
||||
callback.updateNumberOfActiveSyncAssociations(
|
||||
associationInfo.getUserId(), /* added= */ false);
|
||||
} else {
|
||||
Slog.w(TAG, "No callback to report removed transport");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Stable association!
|
||||
final boolean systemBlocked = isAssociationBlocked(associationInfo);
|
||||
if (isAssociationBlockedLocal(associationInfo.getId()) != systemBlocked) {
|
||||
// Block state has changed.
|
||||
final CrossDeviceSyncControllerCallback callback =
|
||||
mInCallServiceCallbackRef != null
|
||||
? mInCallServiceCallbackRef.get() : null;
|
||||
if (!systemBlocked) {
|
||||
Slog.i(TAG, "Unblocking existing association for context sync");
|
||||
mBlocklist.remove(associationInfo.getId());
|
||||
if (callback != null) {
|
||||
callback.updateNumberOfActiveSyncAssociations(
|
||||
associationInfo.getUserId(), /* added= */ true);
|
||||
callback.requestCrossDeviceSync(associationInfo);
|
||||
} else {
|
||||
Slog.w(TAG, "No callback to report changed transport");
|
||||
syncMessageToDevice(associationInfo.getId(),
|
||||
createFacilitatorMessage());
|
||||
}
|
||||
} else {
|
||||
Slog.i(TAG, "Blocking existing association for context sync");
|
||||
mBlocklist.add(associationInfo.getId());
|
||||
if (callback != null) {
|
||||
callback.updateNumberOfActiveSyncAssociations(
|
||||
associationInfo.getUserId(), /* added= */ false);
|
||||
} else {
|
||||
Slog.w(TAG, "No callback to report changed transport");
|
||||
}
|
||||
// Send empty message to device to clear its data (otherwise it
|
||||
// will get stale)
|
||||
syncMessageToDevice(associationInfo.getId(),
|
||||
createEmptyMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,18 +197,48 @@ public class CrossDeviceSyncController {
|
||||
new IOnMessageReceivedListener.Stub() {
|
||||
@Override
|
||||
public void onMessageReceived(int associationId, byte[] data) {
|
||||
if (isAssociationBlockedLocal(associationId)) {
|
||||
return;
|
||||
}
|
||||
final CallMetadataSyncData processedData = processTelecomDataFromSync(data);
|
||||
mPhoneAccountManager.updateFacilitators(associationId, processedData);
|
||||
processCallCreateRequests(associationId, processedData);
|
||||
if (mCrossDeviceSyncControllerCallback == null) {
|
||||
mCallManager.updateCalls(associationId, processedData);
|
||||
processCallCreateRequests(processedData);
|
||||
if (mInCallServiceCallbackRef == null
|
||||
&& mConnectionServiceCallbackRef == null) {
|
||||
Slog.w(TAG, "No callback to process context sync message");
|
||||
return;
|
||||
}
|
||||
mCrossDeviceSyncControllerCallback.processContextSyncMessage(associationId,
|
||||
processedData);
|
||||
final CrossDeviceSyncControllerCallback inCallServiceCallback =
|
||||
mInCallServiceCallbackRef != null ? mInCallServiceCallbackRef.get()
|
||||
: null;
|
||||
if (inCallServiceCallback != null) {
|
||||
inCallServiceCallback.processContextSyncMessage(associationId,
|
||||
processedData);
|
||||
} else {
|
||||
// This is dead; get rid of it lazily
|
||||
mInCallServiceCallbackRef = null;
|
||||
}
|
||||
|
||||
final CrossDeviceSyncControllerCallback connectionServiceCallback =
|
||||
mConnectionServiceCallbackRef != null
|
||||
? mConnectionServiceCallbackRef.get() : null;
|
||||
if (connectionServiceCallback != null) {
|
||||
connectionServiceCallback.processContextSyncMessage(associationId,
|
||||
processedData);
|
||||
} else {
|
||||
// This is dead; get rid of it lazily
|
||||
mConnectionServiceCallbackRef = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
mPhoneAccountManager = new PhoneAccountManager(mContext);
|
||||
mCallManager = new CallManager(mContext, mPhoneAccountManager);
|
||||
}
|
||||
|
||||
private static boolean isAssociationBlocked(AssociationInfo info) {
|
||||
return (info.getSystemDataSyncFlags() & CompanionDeviceManager.FLAG_CALL_METADATA)
|
||||
!= CompanionDeviceManager.FLAG_CALL_METADATA;
|
||||
}
|
||||
|
||||
/** Invoke set-up tasks that happen when boot is completed. */
|
||||
@@ -155,7 +250,7 @@ public class CrossDeviceSyncController {
|
||||
mPhoneAccountManager.onBootCompleted();
|
||||
|
||||
final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
|
||||
if (telecomManager.getCallCapablePhoneAccounts().size() != 0) {
|
||||
if (telecomManager != null && telecomManager.getCallCapablePhoneAccounts().size() != 0) {
|
||||
final PhoneAccountHandle defaultOutgoingTelAccountHandle =
|
||||
telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
|
||||
if (defaultOutgoingTelAccountHandle != null) {
|
||||
@@ -171,8 +266,7 @@ public class CrossDeviceSyncController {
|
||||
}
|
||||
}
|
||||
|
||||
private void processCallCreateRequests(int associationId,
|
||||
CallMetadataSyncData callMetadataSyncData) {
|
||||
private void processCallCreateRequests(CallMetadataSyncData callMetadataSyncData) {
|
||||
final Iterator<CallMetadataSyncData.CallCreateRequest> iterator =
|
||||
callMetadataSyncData.getCallCreateRequests().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
@@ -184,7 +278,7 @@ public class CrossDeviceSyncController {
|
||||
final Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL,
|
||||
request.getAddress().replaceAll("\\D+", ""), /* fragment= */ null);
|
||||
final Bundle extras = new Bundle();
|
||||
extras.putString(CrossDeviceCall.EXTRA_CALL_ID, request.getId());
|
||||
extras.putString(EXTRA_CALL_ID, request.getId());
|
||||
final Bundle outerExtras = new Bundle();
|
||||
outerExtras.putParcelable(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
|
||||
mContext.getSystemService(TelecomManager.class).placeCall(uri, outerExtras);
|
||||
@@ -196,39 +290,33 @@ public class CrossDeviceSyncController {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAssociationBlocked(int associationId) {
|
||||
/**
|
||||
* This keeps track of "previous" state to calculate deltas. Use {@link #isAssociationBlocked}
|
||||
* for all other use cases.
|
||||
*/
|
||||
private boolean isAssociationBlockedLocal(int associationId) {
|
||||
return mBlocklist.contains(associationId);
|
||||
}
|
||||
|
||||
/** Registers the call metadata callback. */
|
||||
public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback) {
|
||||
mCrossDeviceSyncControllerCallback = callback;
|
||||
for (AssociationInfo associationInfo : mConnectedAssociations) {
|
||||
if (!isAssociationBlocked(associationInfo.getId())) {
|
||||
mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
|
||||
associationInfo.getUserId(), /* added= */ true);
|
||||
mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Allow specific associated devices to enable / disable syncing. */
|
||||
public void setSyncEnabled(AssociationInfo associationInfo, boolean enabled) {
|
||||
if (enabled) {
|
||||
if (isAssociationBlocked(associationInfo.getId())) {
|
||||
mBlocklist.remove(associationInfo.getId());
|
||||
mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
|
||||
associationInfo.getUserId(), /* added= */ true);
|
||||
mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
|
||||
public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback,
|
||||
@CrossDeviceSyncControllerCallback.Type int type) {
|
||||
if (type == CrossDeviceSyncControllerCallback.TYPE_IN_CALL_SERVICE) {
|
||||
mInCallServiceCallbackRef = new WeakReference<>(callback);
|
||||
for (AssociationInfo associationInfo : mConnectedAssociations) {
|
||||
if (!isAssociationBlocked(associationInfo)) {
|
||||
mBlocklist.remove(associationInfo.getId());
|
||||
callback.updateNumberOfActiveSyncAssociations(associationInfo.getUserId(),
|
||||
/* added= */ true);
|
||||
callback.requestCrossDeviceSync(associationInfo);
|
||||
} else {
|
||||
mBlocklist.add(associationInfo.getId());
|
||||
}
|
||||
}
|
||||
} else if (type == CrossDeviceSyncControllerCallback.TYPE_CONNECTION_SERVICE) {
|
||||
mConnectionServiceCallbackRef = new WeakReference<>(callback);
|
||||
} else {
|
||||
if (!isAssociationBlocked(associationInfo.getId())) {
|
||||
mBlocklist.add(associationInfo.getId());
|
||||
mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
|
||||
associationInfo.getUserId(), /* added= */ false);
|
||||
// Send empty message to device to clear its data (otherwise it will get stale)
|
||||
syncMessageToDevice(associationInfo.getId(), createEmptyMessage());
|
||||
}
|
||||
Slog.e(TAG, "Cannot register callback of unknown type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,8 +334,7 @@ public class CrossDeviceSyncController {
|
||||
public void syncToAllDevicesForUserId(int userId, Collection<CrossDeviceCall> calls) {
|
||||
final Set<Integer> associationIds = new HashSet<>();
|
||||
for (AssociationInfo associationInfo : mConnectedAssociations) {
|
||||
if (associationInfo.getUserId() == userId && !isAssociationBlocked(
|
||||
associationInfo.getId())) {
|
||||
if (associationInfo.getUserId() == userId && !isAssociationBlocked(associationInfo)) {
|
||||
associationIds.add(associationInfo.getId());
|
||||
}
|
||||
}
|
||||
@@ -269,7 +356,7 @@ public class CrossDeviceSyncController {
|
||||
*/
|
||||
public void syncToSingleDevice(AssociationInfo associationInfo,
|
||||
Collection<CrossDeviceCall> calls) {
|
||||
if (isAssociationBlocked(associationInfo.getId())) {
|
||||
if (isAssociationBlocked(associationInfo)) {
|
||||
Slog.e(TAG, "Cannot sync to requested device; connection is blocked");
|
||||
return;
|
||||
}
|
||||
@@ -286,7 +373,7 @@ public class CrossDeviceSyncController {
|
||||
* @param message The message to sync.
|
||||
*/
|
||||
public void syncMessageToDevice(int associationId, byte[] message) {
|
||||
if (isAssociationBlocked(associationId)) {
|
||||
if (isAssociationBlockedLocal(associationId)) {
|
||||
Slog.e(TAG, "Cannot sync to requested device; connection is blocked");
|
||||
return;
|
||||
}
|
||||
@@ -491,6 +578,10 @@ public class CrossDeviceSyncController {
|
||||
pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION);
|
||||
final long telecomToken = pos.start(ContextSyncMessage.TELECOM);
|
||||
for (CrossDeviceCall call : calls) {
|
||||
if (call.isCallPlacedByContextSync()) {
|
||||
// Do not sync any calls which our "ours" as that would be duplicative.
|
||||
continue;
|
||||
}
|
||||
final long callsToken = pos.start(Telecom.CALLS);
|
||||
pos.write(Telecom.Call.ID, call.getId());
|
||||
final long originToken = pos.start(Telecom.Call.ORIGIN);
|
||||
@@ -559,6 +650,50 @@ public class CrossDeviceSyncController {
|
||||
return pos.getBytes();
|
||||
}
|
||||
|
||||
/** Create a facilitator-only message, used before any calls are available as a call intake. */
|
||||
private byte[] createFacilitatorMessage() {
|
||||
return createCallUpdateMessage(Collections.emptyList(), -1);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class CallManager {
|
||||
|
||||
@VisibleForTesting final Map<Integer, Set<String>> mCallIds = new HashMap<>();
|
||||
private final TelecomManager mTelecomManager;
|
||||
private final PhoneAccountManager mPhoneAccountManager;
|
||||
|
||||
CallManager(Context context, PhoneAccountManager phoneAccountManager) {
|
||||
mTelecomManager = context.getSystemService(TelecomManager.class);
|
||||
mPhoneAccountManager = phoneAccountManager;
|
||||
}
|
||||
|
||||
/** Add any new calls to Telecom. The ConnectionService will handle everything else. */
|
||||
void updateCalls(int associationId, CallMetadataSyncData data) {
|
||||
final Set<String> oldCallIds = mCallIds.getOrDefault(associationId, new HashSet<>());
|
||||
final Set<String> newCallIds = data.getCalls().stream().map(
|
||||
CallMetadataSyncData.Call::getId).collect(Collectors.toSet());
|
||||
if (oldCallIds.equals(newCallIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (CallMetadataSyncData.Call currentCall : data.getCalls()) {
|
||||
if (!oldCallIds.contains(currentCall.getId())
|
||||
&& currentCall.getFacilitator() != null) {
|
||||
final Bundle extras = new Bundle();
|
||||
extras.putInt(EXTRA_ASSOCIATION_ID, associationId);
|
||||
extras.putBoolean(EXTRA_IS_REMOTE_ORIGIN, true);
|
||||
extras.putParcelable(EXTRA_CALL, currentCall);
|
||||
extras.putString(EXTRA_CALL_ID, currentCall.getId());
|
||||
extras.putByteArray(EXTRA_FACILITATOR_ICON, currentCall.getAppIcon());
|
||||
final PhoneAccountHandle handle = mPhoneAccountManager.getPhoneAccountHandle(
|
||||
associationId, currentCall.getFacilitator().getIdentifier());
|
||||
mTelecomManager.addNewIncomingCall(handle, extras);
|
||||
}
|
||||
}
|
||||
mCallIds.put(associationId, newCallIds);
|
||||
}
|
||||
}
|
||||
|
||||
static class PhoneAccountManager {
|
||||
private final Map<PhoneAccountHandleIdentifier, PhoneAccountHandle> mPhoneAccountHandles =
|
||||
new HashMap<>();
|
||||
|
||||
@@ -16,11 +16,25 @@
|
||||
|
||||
package com.android.server.companion.datatransfer.contextsync;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.companion.AssociationInfo;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/** Callback for call metadata syncing. */
|
||||
public abstract class CrossDeviceSyncControllerCallback {
|
||||
|
||||
static final int TYPE_CONNECTION_SERVICE = 1;
|
||||
static final int TYPE_IN_CALL_SERVICE = 2;
|
||||
@IntDef(prefix = { "TYPE_" }, value = {
|
||||
TYPE_CONNECTION_SERVICE,
|
||||
TYPE_IN_CALL_SERVICE,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Type {
|
||||
}
|
||||
|
||||
void processContextSyncMessage(int associationId, CallMetadataSyncData callMetadataSyncData) {}
|
||||
|
||||
void requestCrossDeviceSync(AssociationInfo associationInfo) {}
|
||||
|
||||
@@ -75,7 +75,7 @@ public class CallMetadataSyncConnectionServiceTest {
|
||||
callMetadataSyncData.addCall(call);
|
||||
mSyncConnectionService.mCrossDeviceSyncControllerCallback.processContextSyncMessage(
|
||||
/* associationId= */ 0, callMetadataSyncData);
|
||||
verify(mMockTelecomManager, times(1)).addNewIncomingCall(any(), any());
|
||||
verify(mMockTelecomManager, times(0)).addNewIncomingCall(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.platform.test.annotations.Presubmit;
|
||||
import android.telecom.Call;
|
||||
import android.telecom.ParcelableCall;
|
||||
import android.telecom.PhoneAccountHandle;
|
||||
import android.telecom.TelecomManager;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
@@ -45,7 +46,7 @@ public class CrossDeviceCallTest {
|
||||
@Test
|
||||
public void updateCallDetails_uninitialized() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
|
||||
.isEqualTo(android.companion.Telecom.Call.UNKNOWN_STATUS);
|
||||
@@ -55,7 +56,7 @@ public class CrossDeviceCallTest {
|
||||
@Test
|
||||
public void updateCallDetails_ringing() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
|
||||
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
|
||||
@@ -70,7 +71,7 @@ public class CrossDeviceCallTest {
|
||||
@Test
|
||||
public void updateCallDetails_ongoing() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
|
||||
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
|
||||
@@ -85,7 +86,7 @@ public class CrossDeviceCallTest {
|
||||
@Test
|
||||
public void updateCallDetails_holding() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_HOLDING,
|
||||
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
|
||||
@@ -99,7 +100,7 @@ public class CrossDeviceCallTest {
|
||||
@Test
|
||||
public void updateCallDetails_cannotHold() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_MUTE));
|
||||
@@ -113,7 +114,7 @@ public class CrossDeviceCallTest {
|
||||
@Test
|
||||
public void updateCallDetails_cannotMute() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_HOLD));
|
||||
@@ -127,7 +128,7 @@ public class CrossDeviceCallTest {
|
||||
@Test
|
||||
public void updateCallDetails_transitionRingingToOngoing() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
|
||||
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
|
||||
@@ -150,7 +151,7 @@ public class CrossDeviceCallTest {
|
||||
@Test
|
||||
public void updateSilencedIfRinging_ringing_silenced() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
|
||||
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
|
||||
@@ -165,7 +166,7 @@ public class CrossDeviceCallTest {
|
||||
@Test
|
||||
public void updateSilencedIfRinging_notRinging_notSilenced() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
|
||||
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
|
||||
@@ -179,12 +180,11 @@ public class CrossDeviceCallTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReadableCallerId_enterpriseCall_adminBlocked_ott() {
|
||||
public void getReadableCallerId_enterpriseCall_adminBlocked_hasContact() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.mIsEnterprise = true;
|
||||
crossDeviceCall.mIsOtt = true;
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
|
||||
|
||||
@@ -195,76 +195,74 @@ public class CrossDeviceCallTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReadableCallerId_enterpriseCall_adminUnblocked_ott() {
|
||||
public void getReadableCallerId_enterpriseCall_adminUnblocked_hasContact() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.mIsEnterprise = true;
|
||||
crossDeviceCall.mIsOtt = true;
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
|
||||
|
||||
final String result = crossDeviceCall.getReadableCallerId(false);
|
||||
|
||||
assertWithMessage("Wrong caller id").that(result)
|
||||
.isEqualTo(CONTACT_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReadableCallerId_enterpriseCall_adminBlocked_noContact() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.mIsEnterprise = true;
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0, /* hasContactName= */
|
||||
false));
|
||||
|
||||
final String result = crossDeviceCall.getReadableCallerId(true);
|
||||
|
||||
assertWithMessage("Wrong caller id").that(result)
|
||||
.isEqualTo(CALLER_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReadableCallerId_nonEnterpriseCall_adminBlocked_noContact() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.mIsEnterprise = false;
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0, /* hasContactName= */
|
||||
false));
|
||||
|
||||
final String result = crossDeviceCall.getReadableCallerId(true);
|
||||
|
||||
assertWithMessage("Wrong caller id").that(result)
|
||||
.isEqualTo(CALLER_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_noContact() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.mIsEnterprise = false;
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0, /* hasContactName= */
|
||||
false));
|
||||
|
||||
final String result = crossDeviceCall.getReadableCallerId(false);
|
||||
|
||||
assertWithMessage("Wrong caller id").that(result)
|
||||
.isEqualTo(CALLER_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReadableCallerId_enterpriseCall_adminBlocked_pstn() {
|
||||
public void getReadableCallerId_nonEnterpriseCall_adminBlocked_hasContact() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.mIsEnterprise = true;
|
||||
crossDeviceCall.mIsOtt = false;
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
|
||||
|
||||
final String result = crossDeviceCall.getReadableCallerId(true);
|
||||
|
||||
assertWithMessage("Wrong caller id").that(result)
|
||||
.isEqualTo(CALLER_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReadableCallerId_nonEnterpriseCall_adminBlocked_ott() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.mIsEnterprise = false;
|
||||
crossDeviceCall.mIsOtt = true;
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
|
||||
|
||||
final String result = crossDeviceCall.getReadableCallerId(true);
|
||||
|
||||
assertWithMessage("Wrong caller id").that(result)
|
||||
.isEqualTo(CALLER_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_ott() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.mIsEnterprise = false;
|
||||
crossDeviceCall.mIsOtt = true;
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
|
||||
|
||||
final String result = crossDeviceCall.getReadableCallerId(false);
|
||||
|
||||
assertWithMessage("Wrong caller id").that(result)
|
||||
.isEqualTo(CALLER_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReadableCallerId_nonEnterpriseCall_adminBlocked_pstn() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.mIsEnterprise = false;
|
||||
crossDeviceCall.mIsOtt = false;
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
|
||||
|
||||
@@ -275,12 +273,11 @@ public class CrossDeviceCallTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_pstn() {
|
||||
public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_hasContact() {
|
||||
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
|
||||
InstrumentationRegistry.getTargetContext().getPackageManager(),
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
mUninitializedCallDetails, /* callAudioState= */ null);
|
||||
crossDeviceCall.mIsEnterprise = false;
|
||||
crossDeviceCall.mIsOtt = false;
|
||||
crossDeviceCall.updateCallDetails(
|
||||
createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
|
||||
|
||||
@@ -291,10 +288,17 @@ public class CrossDeviceCallTest {
|
||||
}
|
||||
|
||||
private Call.Details createCallDetails(int state, int capabilities) {
|
||||
return createCallDetails(state, capabilities, /* hasContactName= */ true);
|
||||
}
|
||||
|
||||
private Call.Details createCallDetails(int state, int capabilities, boolean hasContactName) {
|
||||
final ParcelableCall.ParcelableCallBuilder parcelableCallBuilder =
|
||||
new ParcelableCall.ParcelableCallBuilder();
|
||||
parcelableCallBuilder.setCallerDisplayName(CALLER_DISPLAY_NAME);
|
||||
parcelableCallBuilder.setContactDisplayName(CONTACT_DISPLAY_NAME);
|
||||
if (hasContactName) {
|
||||
parcelableCallBuilder.setContactDisplayName(CONTACT_DISPLAY_NAME);
|
||||
}
|
||||
parcelableCallBuilder.setCallerDisplayNamePresentation(TelecomManager.PRESENTATION_ALLOWED);
|
||||
parcelableCallBuilder.setCapabilities(capabilities);
|
||||
parcelableCallBuilder.setState(state);
|
||||
parcelableCallBuilder.setConferenceableCallIds(Collections.emptyList());
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -211,4 +212,62 @@ public class CrossDeviceSyncControllerTest {
|
||||
verify(mMockTelecomManager, times(1)).registerPhoneAccount(any());
|
||||
verify(mMockTelecomManager, times(1)).unregisterPhoneAccount(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCalls_newCall() {
|
||||
final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
|
||||
call.setId("123abc");
|
||||
call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test"));
|
||||
final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData();
|
||||
callMetadataSyncData.addCall(call);
|
||||
final CrossDeviceSyncController.CallManager callManager =
|
||||
new CrossDeviceSyncController.CallManager(mMockContext,
|
||||
new CrossDeviceSyncController.PhoneAccountManager(mMockContext));
|
||||
callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData);
|
||||
verify(mMockTelecomManager, times(1)).addNewIncomingCall(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCalls_newCall_noFacilitator() {
|
||||
final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
|
||||
call.setId("123abc");
|
||||
final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData();
|
||||
callMetadataSyncData.addCall(call);
|
||||
final CrossDeviceSyncController.CallManager callManager =
|
||||
new CrossDeviceSyncController.CallManager(mMockContext,
|
||||
new CrossDeviceSyncController.PhoneAccountManager(mMockContext));
|
||||
callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData);
|
||||
verify(mMockTelecomManager, times(0)).addNewIncomingCall(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCalls_existingCall() {
|
||||
final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
|
||||
call.setId("123abc");
|
||||
final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData();
|
||||
callMetadataSyncData.addCall(call);
|
||||
final CrossDeviceSyncController.CallManager callManager =
|
||||
new CrossDeviceSyncController.CallManager(mMockContext,
|
||||
new CrossDeviceSyncController.PhoneAccountManager(mMockContext));
|
||||
callManager.mCallIds.put(/* associationId= */ 0, Set.of(call.getId()));
|
||||
callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData);
|
||||
verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCalls_removedCall() {
|
||||
final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
|
||||
call.setId("123abc");
|
||||
final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData();
|
||||
callMetadataSyncData.addCall(call);
|
||||
final CrossDeviceSyncController.CallManager callManager =
|
||||
new CrossDeviceSyncController.CallManager(mMockContext,
|
||||
new CrossDeviceSyncController.PhoneAccountManager(mMockContext));
|
||||
callManager.mCallIds.put(/* associationId= */ 0, Set.of(call.getId(), "fakeCallId"));
|
||||
callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData);
|
||||
verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any());
|
||||
assertWithMessage("Hasn't removed the id of the removed call")
|
||||
.that(callManager.mCallIds)
|
||||
.containsExactly(/* associationId= */ 0, Set.of(call.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user