Merge "Add simulate-device-(dis)appeared CDM shell commands" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
8b0ef79199
@@ -732,9 +732,12 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
String[] args, ShellCallback callback, ResultReceiver resultReceiver)
|
String[] args, ShellCallback callback, ResultReceiver resultReceiver)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand");
|
enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand");
|
||||||
new CompanionDeviceShellCommand(
|
|
||||||
CompanionDeviceManagerService.this, mAssociationStore)
|
final CompanionDeviceShellCommand cmd = new CompanionDeviceShellCommand(
|
||||||
.exec(this, in, out, err, args, callback, resultReceiver);
|
CompanionDeviceManagerService.this,
|
||||||
|
mAssociationStore,
|
||||||
|
mDevicePresenceMonitor);
|
||||||
|
cmd.exec(this, in, out, err, args, callback, resultReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import android.os.ShellCommand;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -29,20 +31,24 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
|||||||
|
|
||||||
private final CompanionDeviceManagerService mService;
|
private final CompanionDeviceManagerService mService;
|
||||||
private final AssociationStore mAssociationStore;
|
private final AssociationStore mAssociationStore;
|
||||||
|
private final CompanionDevicePresenceMonitor mDevicePresenceMonitor;
|
||||||
|
|
||||||
CompanionDeviceShellCommand(CompanionDeviceManagerService service,
|
CompanionDeviceShellCommand(CompanionDeviceManagerService service,
|
||||||
AssociationStore associationStore) {
|
AssociationStore associationStore,
|
||||||
|
CompanionDevicePresenceMonitor devicePresenceMonitor) {
|
||||||
mService = service;
|
mService = service;
|
||||||
mAssociationStore = associationStore;
|
mAssociationStore = associationStore;
|
||||||
|
mDevicePresenceMonitor = devicePresenceMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onCommand(String cmd) {
|
public int onCommand(String cmd) {
|
||||||
final PrintWriter out = getOutPrintWriter();
|
final PrintWriter out = getOutPrintWriter();
|
||||||
|
final int associationId;
|
||||||
try {
|
try {
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case "list": {
|
case "list": {
|
||||||
final int userId = getNextArgInt();
|
final int userId = getNextIntArgRequired();
|
||||||
final List<AssociationInfo> associationsForUser =
|
final List<AssociationInfo> associationsForUser =
|
||||||
mAssociationStore.getAssociationsForUser(userId);
|
mAssociationStore.getAssociationsForUser(userId);
|
||||||
for (AssociationInfo association : associationsForUser) {
|
for (AssociationInfo association : associationsForUser) {
|
||||||
@@ -55,7 +61,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "associate": {
|
case "associate": {
|
||||||
int userId = getNextArgInt();
|
int userId = getNextIntArgRequired();
|
||||||
String packageName = getNextArgRequired();
|
String packageName = getNextArgRequired();
|
||||||
String address = getNextArgRequired();
|
String address = getNextArgRequired();
|
||||||
mService.legacyCreateAssociation(userId, address, packageName, null);
|
mService.legacyCreateAssociation(userId, address, packageName, null);
|
||||||
@@ -63,7 +69,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "disassociate": {
|
case "disassociate": {
|
||||||
final int userId = getNextArgInt();
|
final int userId = getNextIntArgRequired();
|
||||||
final String packageName = getNextArgRequired();
|
final String packageName = getNextArgRequired();
|
||||||
final String address = getNextArgRequired();
|
final String address = getNextArgRequired();
|
||||||
final AssociationInfo association =
|
final AssociationInfo association =
|
||||||
@@ -80,6 +86,16 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "simulate-device-appeared":
|
||||||
|
associationId = getNextIntArgRequired();
|
||||||
|
mDevicePresenceMonitor.simulateDeviceAppeared(associationId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "simulate-device-disappeared":
|
||||||
|
associationId = getNextIntArgRequired();
|
||||||
|
mDevicePresenceMonitor.simulateDeviceDisappeared(associationId);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return handleDefaultCommands(cmd);
|
return handleDefaultCommands(cmd);
|
||||||
}
|
}
|
||||||
@@ -91,10 +107,6 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getNextArgInt() {
|
|
||||||
return Integer.parseInt(getNextArgRequired());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onHelp() {
|
public void onHelp() {
|
||||||
PrintWriter pw = getOutPrintWriter();
|
PrintWriter pw = getOutPrintWriter();
|
||||||
@@ -108,7 +120,31 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
|||||||
pw.println(" disassociate USER_ID PACKAGE MAC_ADDRESS");
|
pw.println(" disassociate USER_ID PACKAGE MAC_ADDRESS");
|
||||||
pw.println(" Remove an existing Association.");
|
pw.println(" Remove an existing Association.");
|
||||||
pw.println(" clear-association-memory-cache");
|
pw.println(" clear-association-memory-cache");
|
||||||
pw.println(" Clear the in-memory association cache and reload all association "
|
pw.println(" Clear the in-memory association cache and reload all association ");
|
||||||
+ "information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
|
pw.println(" information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
|
||||||
|
pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
|
||||||
|
|
||||||
|
pw.println(" simulate-device-appeared ASSOCIATION_ID");
|
||||||
|
pw.println(" Make CDM act as if the given companion device has appeared.");
|
||||||
|
pw.println(" I.e. bind the associated companion application's");
|
||||||
|
pw.println(" CompanionDeviceService(s) and trigger onDeviceAppeared() callback.");
|
||||||
|
pw.println(" The CDM will consider the devices as present for 60 seconds and then");
|
||||||
|
pw.println(" will act as if device disappeared, unless 'simulate-device-disappeared'");
|
||||||
|
pw.println(" or 'simulate-device-appeared' is called again before 60 seconds run out"
|
||||||
|
+ ".");
|
||||||
|
pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
|
||||||
|
|
||||||
|
pw.println(" simulate-device-disappeared ASSOCIATION_ID");
|
||||||
|
pw.println(" Make CDM act as if the given companion device has disappeared.");
|
||||||
|
pw.println(" I.e. unbind the associated companion application's");
|
||||||
|
pw.println(" CompanionDeviceService(s) and trigger onDeviceDisappeared() callback.");
|
||||||
|
pw.println(" NOTE: This will only have effect if 'simulate-device-appeared' was");
|
||||||
|
pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than");
|
||||||
|
pw.println(" 60 seconds ago.");
|
||||||
|
pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getNextIntArgRequired() {
|
||||||
|
return Integer.parseInt(getNextArgRequired());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,19 @@
|
|||||||
|
|
||||||
package com.android.server.companion.presence;
|
package com.android.server.companion.presence;
|
||||||
|
|
||||||
|
import static android.os.Process.ROOT_UID;
|
||||||
|
import static android.os.Process.SHELL_UID;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.annotation.TestApi;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.companion.AssociationInfo;
|
import android.companion.AssociationInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.server.companion.AssociationStore;
|
import com.android.server.companion.AssociationStore;
|
||||||
@@ -72,6 +80,11 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
|
|||||||
private final @NonNull Set<Integer> mNearbyBleDevices = new HashSet<>();
|
private final @NonNull Set<Integer> mNearbyBleDevices = new HashSet<>();
|
||||||
private final @NonNull Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
|
private final @NonNull Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
|
||||||
|
|
||||||
|
// Tracking "simulated" presence. Used for debugging and testing only.
|
||||||
|
private final @NonNull Set<Integer> mSimulated = new HashSet<>();
|
||||||
|
private final SimulatedDevicePresenceSchedulerHelper mSchedulerHelper =
|
||||||
|
new SimulatedDevicePresenceSchedulerHelper();
|
||||||
|
|
||||||
public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore,
|
public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore,
|
||||||
@NonNull Callback callback) {
|
@NonNull Callback callback) {
|
||||||
mAssociationStore = associationStore;
|
mAssociationStore = associationStore;
|
||||||
@@ -106,7 +119,8 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
|
|||||||
public boolean isDevicePresent(int associationId) {
|
public boolean isDevicePresent(int associationId) {
|
||||||
return mReportedSelfManagedDevices.contains(associationId)
|
return mReportedSelfManagedDevices.contains(associationId)
|
||||||
|| mConnectedBtDevices.contains(associationId)
|
|| mConnectedBtDevices.contains(associationId)
|
||||||
|| mNearbyBleDevices.contains(associationId);
|
|| mNearbyBleDevices.contains(associationId)
|
||||||
|
|| mSimulated.contains(associationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,6 +169,45 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
|
|||||||
onDeviceGone(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble");
|
onDeviceGone(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** FOR DEBUGGING AND/OR TESTING PURPOSES ONLY. */
|
||||||
|
@TestApi
|
||||||
|
public void simulateDeviceAppeared(int associationId) {
|
||||||
|
// IMPORTANT: this API should only be invoked via the
|
||||||
|
// 'companiondevice simulate-device-appeared' Shell command, so the only uid-s allowed to
|
||||||
|
// make this call are SHELL and ROOT.
|
||||||
|
// No other caller (including SYSTEM!) should be allowed.
|
||||||
|
enforceCallerShellOrRoot();
|
||||||
|
// Make sure the association exists.
|
||||||
|
enforceAssociationExists(associationId);
|
||||||
|
|
||||||
|
onDevicePresent(mSimulated, associationId, /* sourceLoggingTag */ "simulated");
|
||||||
|
|
||||||
|
mSchedulerHelper.scheduleOnDeviceGoneCallForSimulatedDevicePresence(associationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** FOR DEBUGGING AND/OR TESTING PURPOSES ONLY. */
|
||||||
|
@TestApi
|
||||||
|
public void simulateDeviceDisappeared(int associationId) {
|
||||||
|
// IMPORTANT: this API should only be invoked via the
|
||||||
|
// 'companiondevice simulate-device-appeared' Shell command, so the only uid-s allowed to
|
||||||
|
// make this call are SHELL and ROOT.
|
||||||
|
// No other caller (including SYSTEM!) should be allowed.
|
||||||
|
enforceCallerShellOrRoot();
|
||||||
|
// Make sure the association exists.
|
||||||
|
enforceAssociationExists(associationId);
|
||||||
|
|
||||||
|
mSchedulerHelper.unscheduleOnDeviceGoneCallForSimulatedDevicePresence(associationId);
|
||||||
|
|
||||||
|
onDeviceGone(mSimulated, associationId, /* sourceLoggingTag */ "simulated");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enforceAssociationExists(int associationId) {
|
||||||
|
if (mAssociationStore.getAssociationById(associationId) == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Association with id " + associationId + " does not exist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void onDevicePresent(@NonNull Set<Integer> presentDevicesForSource,
|
private void onDevicePresent(@NonNull Set<Integer> presentDevicesForSource,
|
||||||
int newDeviceAssociationId, @NonNull String sourceLoggingTag) {
|
int newDeviceAssociationId, @NonNull String sourceLoggingTag) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -212,6 +265,7 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
|
|||||||
mConnectedBtDevices.remove(associationId);
|
mConnectedBtDevices.remove(associationId);
|
||||||
mNearbyBleDevices.remove(associationId);
|
mNearbyBleDevices.remove(associationId);
|
||||||
mReportedSelfManagedDevices.remove(associationId);
|
mReportedSelfManagedDevices.remove(associationId);
|
||||||
|
mSimulated.remove(associationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,4 +286,36 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
|
|||||||
// CompanionDeviceManagerService will know that the association is removed, and will do
|
// CompanionDeviceManagerService will know that the association is removed, and will do
|
||||||
// what's needed.
|
// what's needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void enforceCallerShellOrRoot() {
|
||||||
|
final int callingUid = Binder.getCallingUid();
|
||||||
|
if (callingUid == SHELL_UID || callingUid == ROOT_UID) return;
|
||||||
|
|
||||||
|
throw new SecurityException("Caller is neither Shell nor Root");
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SimulatedDevicePresenceSchedulerHelper extends Handler {
|
||||||
|
SimulatedDevicePresenceSchedulerHelper() {
|
||||||
|
super(Looper.getMainLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
void scheduleOnDeviceGoneCallForSimulatedDevicePresence(int associationId) {
|
||||||
|
// First, unschedule if it was scheduled previously.
|
||||||
|
if (hasMessages(/* what */ associationId)) {
|
||||||
|
removeMessages(/* what */ associationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEmptyMessageDelayed(/* what */ associationId, 60 * 1000 /* 60 seconds */);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unscheduleOnDeviceGoneCallForSimulatedDevicePresence(int associationId) {
|
||||||
|
removeMessages(/* what */ associationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(@NonNull Message msg) {
|
||||||
|
final int associationId = msg.what;
|
||||||
|
onDeviceGone(mSimulated, associationId, /* sourceLoggingTag */ "simulated");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user