206 lines
7.1 KiB
Java
206 lines
7.1 KiB
Java
|
/*
|
||
|
* Copyright (C) 2022 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package com.android.server.bluetooth;
|
||
|
|
||
|
import static java.util.Objects.requireNonNull;
|
||
|
|
||
|
import android.bluetooth.BluetoothAdapter;
|
||
|
import android.content.AttributionSource;
|
||
|
import android.content.Context;
|
||
|
import android.os.Binder;
|
||
|
import android.os.Process;
|
||
|
import android.os.RemoteException;
|
||
|
import android.util.Log;
|
||
|
|
||
|
import com.android.internal.annotations.VisibleForTesting;
|
||
|
import com.android.modules.utils.BasicShellCommandHandler;
|
||
|
|
||
|
import java.io.PrintWriter;
|
||
|
|
||
|
class BluetoothShellCommand extends BasicShellCommandHandler {
|
||
|
private static final String TAG = BluetoothShellCommand.class.getSimpleName();
|
||
|
|
||
|
private final BluetoothManagerService mManagerService;
|
||
|
private final Context mContext;
|
||
|
|
||
|
@VisibleForTesting
|
||
|
final BluetoothCommand[] mBluetoothCommands = {
|
||
|
new Enable(),
|
||
|
new Disable(),
|
||
|
new WaitForAdapterState(),
|
||
|
};
|
||
|
|
||
|
@VisibleForTesting
|
||
|
abstract class BluetoothCommand {
|
||
|
final boolean mIsPrivileged;
|
||
|
final String mName;
|
||
|
|
||
|
BluetoothCommand(boolean isPrivileged, String name) {
|
||
|
mIsPrivileged = isPrivileged;
|
||
|
mName = requireNonNull(name, "Command name cannot be null");
|
||
|
}
|
||
|
|
||
|
String getName() {
|
||
|
return mName;
|
||
|
}
|
||
|
boolean isMatch(String cmd) {
|
||
|
return mName.equals(cmd);
|
||
|
}
|
||
|
boolean isPrivileged() {
|
||
|
return mIsPrivileged;
|
||
|
}
|
||
|
|
||
|
abstract int exec(String cmd) throws RemoteException;
|
||
|
abstract void onHelp(PrintWriter pw);
|
||
|
}
|
||
|
|
||
|
@VisibleForTesting
|
||
|
class Enable extends BluetoothCommand {
|
||
|
Enable() {
|
||
|
super(false, "enable");
|
||
|
}
|
||
|
@Override
|
||
|
public int exec(String cmd) throws RemoteException {
|
||
|
return mManagerService.enable(AttributionSource.myAttributionSource()) ? 0 : -1;
|
||
|
}
|
||
|
@Override
|
||
|
public void onHelp(PrintWriter pw) {
|
||
|
pw.println(" " + getName());
|
||
|
pw.println(" Enable Bluetooth on this device.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@VisibleForTesting
|
||
|
class Disable extends BluetoothCommand {
|
||
|
Disable() {
|
||
|
super(false, "disable");
|
||
|
}
|
||
|
@Override
|
||
|
public int exec(String cmd) throws RemoteException {
|
||
|
return mManagerService.disable(AttributionSource.myAttributionSource(), true) ? 0 : -1;
|
||
|
}
|
||
|
@Override
|
||
|
public void onHelp(PrintWriter pw) {
|
||
|
pw.println(" " + getName());
|
||
|
pw.println(" Disable Bluetooth on this device.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@VisibleForTesting
|
||
|
class WaitForAdapterState extends BluetoothCommand {
|
||
|
WaitForAdapterState() {
|
||
|
super(false, "wait-for-state");
|
||
|
}
|
||
|
private int getWaitingState(String in) {
|
||
|
if (!in.startsWith(getName() + ":")) return -1;
|
||
|
String[] split = in.split(":", 2);
|
||
|
if (split.length != 2 || !getName().equals(split[0])) {
|
||
|
String msg = getName() + ": Invalid state format: " + in;
|
||
|
Log.e(TAG, msg);
|
||
|
PrintWriter pw = getErrPrintWriter();
|
||
|
pw.println(TAG + ": " + msg);
|
||
|
printHelp(pw);
|
||
|
throw new IllegalArgumentException();
|
||
|
}
|
||
|
switch (split[1]) {
|
||
|
case "STATE_OFF":
|
||
|
return BluetoothAdapter.STATE_OFF;
|
||
|
case "STATE_ON":
|
||
|
return BluetoothAdapter.STATE_ON;
|
||
|
default:
|
||
|
String msg = getName() + ": Invalid state value: " + split[1] + ". From: " + in;
|
||
|
Log.e(TAG, msg);
|
||
|
PrintWriter pw = getErrPrintWriter();
|
||
|
pw.println(TAG + ": " + msg);
|
||
|
printHelp(pw);
|
||
|
throw new IllegalArgumentException();
|
||
|
}
|
||
|
}
|
||
|
@Override
|
||
|
boolean isMatch(String cmd) {
|
||
|
return getWaitingState(cmd) != -1;
|
||
|
}
|
||
|
@Override
|
||
|
public int exec(String cmd) throws RemoteException {
|
||
|
int ret = mManagerService.waitForManagerState(getWaitingState(cmd)) ? 0 : -1;
|
||
|
Log.d(TAG, cmd + ": Return value is " + ret); // logging as this method can take time
|
||
|
return ret;
|
||
|
}
|
||
|
@Override
|
||
|
public void onHelp(PrintWriter pw) {
|
||
|
pw.println(" " + getName() + ":<STATE>");
|
||
|
pw.println(" Wait until the adapter state is <STATE>."
|
||
|
+ " <STATE> can be one of STATE_OFF | STATE_ON");
|
||
|
pw.println(" Note: This command can timeout and failed");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BluetoothShellCommand(BluetoothManagerService managerService, Context context) {
|
||
|
mManagerService = managerService;
|
||
|
mContext = context;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int onCommand(String cmd) {
|
||
|
if (cmd == null) return handleDefaultCommands(null);
|
||
|
|
||
|
for (BluetoothCommand bt_cmd : mBluetoothCommands) {
|
||
|
if (!bt_cmd.isMatch(cmd)) continue;
|
||
|
if (bt_cmd.isPrivileged()) {
|
||
|
final int uid = Binder.getCallingUid();
|
||
|
if (uid != Process.ROOT_UID) {
|
||
|
throw new SecurityException("Uid " + uid + " does not have access to "
|
||
|
+ cmd + " bluetooth command");
|
||
|
}
|
||
|
}
|
||
|
try {
|
||
|
getOutPrintWriter().println(TAG + ": Exec" + cmd);
|
||
|
Log.d(TAG, "Exec " + cmd);
|
||
|
int ret = bt_cmd.exec(cmd);
|
||
|
if (ret == 0) {
|
||
|
String msg = cmd + ": Success";
|
||
|
Log.d(TAG, msg);
|
||
|
getOutPrintWriter().println(msg);
|
||
|
} else {
|
||
|
String msg = cmd + ": Failed with status=" + ret;
|
||
|
Log.e(TAG, msg);
|
||
|
getErrPrintWriter().println(TAG + ": " + msg);
|
||
|
}
|
||
|
return ret;
|
||
|
} catch (RemoteException e) {
|
||
|
Log.w(TAG, cmd + ": error\nException: " + e.getMessage());
|
||
|
getErrPrintWriter().println(cmd + ": error\nException: " + e.getMessage());
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
return handleDefaultCommands(cmd);
|
||
|
}
|
||
|
|
||
|
private void printHelp(PrintWriter pw) {
|
||
|
pw.println("Bluetooth Manager Commands:");
|
||
|
pw.println(" help or -h");
|
||
|
pw.println(" Print this help text.");
|
||
|
for (BluetoothCommand bt_cmd : mBluetoothCommands) {
|
||
|
bt_cmd.onHelp(pw);
|
||
|
}
|
||
|
}
|
||
|
@Override
|
||
|
public void onHelp() {
|
||
|
printHelp(getOutPrintWriter());
|
||
|
}
|
||
|
}
|