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)
|
||||
throws RemoteException {
|
||||
enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand");
|
||||
new CompanionDeviceShellCommand(
|
||||
CompanionDeviceManagerService.this, mAssociationStore)
|
||||
.exec(this, in, out, err, args, callback, resultReceiver);
|
||||
|
||||
final CompanionDeviceShellCommand cmd = new CompanionDeviceShellCommand(
|
||||
CompanionDeviceManagerService.this,
|
||||
mAssociationStore,
|
||||
mDevicePresenceMonitor);
|
||||
cmd.exec(this, in, out, err, args, callback, resultReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.os.ShellCommand;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
|
||||
@@ -29,20 +31,24 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
||||
|
||||
private final CompanionDeviceManagerService mService;
|
||||
private final AssociationStore mAssociationStore;
|
||||
private final CompanionDevicePresenceMonitor mDevicePresenceMonitor;
|
||||
|
||||
CompanionDeviceShellCommand(CompanionDeviceManagerService service,
|
||||
AssociationStore associationStore) {
|
||||
AssociationStore associationStore,
|
||||
CompanionDevicePresenceMonitor devicePresenceMonitor) {
|
||||
mService = service;
|
||||
mAssociationStore = associationStore;
|
||||
mDevicePresenceMonitor = devicePresenceMonitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onCommand(String cmd) {
|
||||
final PrintWriter out = getOutPrintWriter();
|
||||
final int associationId;
|
||||
try {
|
||||
switch (cmd) {
|
||||
case "list": {
|
||||
final int userId = getNextArgInt();
|
||||
final int userId = getNextIntArgRequired();
|
||||
final List<AssociationInfo> associationsForUser =
|
||||
mAssociationStore.getAssociationsForUser(userId);
|
||||
for (AssociationInfo association : associationsForUser) {
|
||||
@@ -55,7 +61,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
||||
break;
|
||||
|
||||
case "associate": {
|
||||
int userId = getNextArgInt();
|
||||
int userId = getNextIntArgRequired();
|
||||
String packageName = getNextArgRequired();
|
||||
String address = getNextArgRequired();
|
||||
mService.legacyCreateAssociation(userId, address, packageName, null);
|
||||
@@ -63,7 +69,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
||||
break;
|
||||
|
||||
case "disassociate": {
|
||||
final int userId = getNextArgInt();
|
||||
final int userId = getNextIntArgRequired();
|
||||
final String packageName = getNextArgRequired();
|
||||
final String address = getNextArgRequired();
|
||||
final AssociationInfo association =
|
||||
@@ -80,6 +86,16 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
||||
}
|
||||
break;
|
||||
|
||||
case "simulate-device-appeared":
|
||||
associationId = getNextIntArgRequired();
|
||||
mDevicePresenceMonitor.simulateDeviceAppeared(associationId);
|
||||
break;
|
||||
|
||||
case "simulate-device-disappeared":
|
||||
associationId = getNextIntArgRequired();
|
||||
mDevicePresenceMonitor.simulateDeviceDisappeared(associationId);
|
||||
break;
|
||||
|
||||
default:
|
||||
return handleDefaultCommands(cmd);
|
||||
}
|
||||
@@ -91,10 +107,6 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private int getNextArgInt() {
|
||||
return Integer.parseInt(getNextArgRequired());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHelp() {
|
||||
PrintWriter pw = getOutPrintWriter();
|
||||
@@ -108,7 +120,31 @@ class CompanionDeviceShellCommand extends ShellCommand {
|
||||
pw.println(" disassociate USER_ID PACKAGE MAC_ADDRESS");
|
||||
pw.println(" Remove an existing Association.");
|
||||
pw.println(" clear-association-memory-cache");
|
||||
pw.println(" Clear the in-memory association cache and reload all association "
|
||||
+ "information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
|
||||
pw.println(" Clear the in-memory association cache and reload all association ");
|
||||
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;
|
||||
|
||||
import static android.os.Process.ROOT_UID;
|
||||
import static android.os.Process.SHELL_UID;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TestApi;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.companion.AssociationInfo;
|
||||
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 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> 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,
|
||||
@NonNull Callback callback) {
|
||||
mAssociationStore = associationStore;
|
||||
@@ -106,7 +119,8 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
|
||||
public boolean isDevicePresent(int associationId) {
|
||||
return mReportedSelfManagedDevices.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");
|
||||
}
|
||||
|
||||
/** 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,
|
||||
int newDeviceAssociationId, @NonNull String sourceLoggingTag) {
|
||||
if (DEBUG) {
|
||||
@@ -212,6 +265,7 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
|
||||
mConnectedBtDevices.remove(associationId);
|
||||
mNearbyBleDevices.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
|
||||
// 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