diff options
-rw-r--r-- | service/Android.bp | 1 | ||||
-rw-r--r-- | service/java/com/android/server/bluetooth/BluetoothManagerService.java | 11 | ||||
-rw-r--r-- | service/java/com/android/server/bluetooth/BluetoothShellCommand.java | 125 |
3 files changed, 136 insertions, 1 deletions
diff --git a/service/Android.bp b/service/Android.bp index c720f6ae6c..f16f25b1bf 100644 --- a/service/Android.bp +++ b/service/Android.bp @@ -67,6 +67,7 @@ java_library { static_libs: [ "androidx.annotation_annotation", "androidx.appcompat_appcompat", + "modules-utils-shell-command-handler", ], apex_available: [ diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 979c747e56..e2fc938efd 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -68,6 +68,7 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.PowerExemptionManager; import android.os.Process; import android.os.RemoteCallbackList; @@ -1019,7 +1020,7 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { // Check if packageName belongs to callingUid final int callingUid = Binder.getCallingUid(); final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if (!isCallerSystem) { + if (!isCallerSystem && callingUid != Process.SHELL_UID) { checkPackage(callingUid, attributionSource.getPackageName()); if (requireForeground && !checkIfCallerIsForegroundUser()) { @@ -3018,6 +3019,14 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { attributionSource, message); } + @Override + public int handleShellCommand(@NonNull ParcelFileDescriptor in, + @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, + @NonNull String[] args) { + return new BluetoothShellCommand(this, mContext).exec(this, in.getFileDescriptor(), + out.getFileDescriptor(), err.getFileDescriptor(), args); + } + static @NonNull Bundle getTempAllowlistBroadcastOptions() { final long duration = 10_000; final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); diff --git a/service/java/com/android/server/bluetooth/BluetoothShellCommand.java b/service/java/com/android/server/bluetooth/BluetoothShellCommand.java new file mode 100644 index 0000000000..2946650f7b --- /dev/null +++ b/service/java/com/android/server/bluetooth/BluetoothShellCommand.java @@ -0,0 +1,125 @@ +/* + * 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 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.modules.utils.BasicShellCommandHandler; + +import java.io.PrintWriter; + +class BluetoothShellCommand extends BasicShellCommandHandler { + private static final String TAG = "BluetoothShellCommand"; + + private final BluetoothManagerService mManagerService; + private final Context mContext; + + private final BluetoothCommand[] mBluetoothCommands = { + new Enable(), + new Disable(), + }; + + private abstract class BluetoothCommand { + abstract String getName(); + // require root permission by default, can be override in command implementation + boolean isPrivileged() { + return true; + } + abstract int exec(PrintWriter pw) throws RemoteException; + } + + private class Enable extends BluetoothCommand { + @Override + String getName() { + return "enable"; + } + @Override + boolean isPrivileged() { + return false; + } + @Override + public int exec(PrintWriter pw) throws RemoteException { + pw.println("Enabling Bluetooth"); + return mManagerService.enable(AttributionSource.myAttributionSource()) ? 0 : -1; + } + } + + private class Disable extends BluetoothCommand { + @Override + String getName() { + return "disable"; + } + @Override + boolean isPrivileged() { + return false; + } + @Override + public int exec(PrintWriter pw) throws RemoteException { + pw.println("Disabling Bluetooth"); + return mManagerService.disable(AttributionSource.myAttributionSource(), true) ? 0 : -1; + } + } + + 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 (cmd.equals(bt_cmd.getName())) { + 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 (or such command doesn't exist)"); + } + } + try { + return bt_cmd.exec(getOutPrintWriter()); + } catch (RemoteException e) { + Log.w(TAG, cmd + ": error\nException: " + e.getMessage()); + getErrPrintWriter().println(cmd + ": error\nException: " + e.getMessage()); + e.rethrowFromSystemServer(); + } + } + } + return handleDefaultCommands(cmd); + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + pw.println("Bluetooth Commands:"); + pw.println(" help or -h"); + pw.println(" Print this help text."); + pw.println(" enable"); + pw.println(" Enable Bluetooth on this device."); + pw.println(" disable"); + pw.println(" Disable Bluetooth on this device."); + } +} |