1 /* 2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.bluetooth; 18 19 import static android.Manifest.permission.DUMP; 20 import static android.Manifest.permission.LOCAL_MAC_ADDRESS; 21 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 22 23 import static com.android.server.bluetooth.BtPermissionUtils.checkConnectPermissionForDataDelivery; 24 import static com.android.server.bluetooth.BtPermissionUtils.getCallingAppId; 25 import static com.android.server.bluetooth.BtPermissionUtils.isCallerSystem; 26 27 import static java.util.Objects.requireNonNull; 28 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.app.AppOpsManager; 32 import android.bluetooth.BluetoothAdapter; 33 import android.bluetooth.IBluetooth; 34 import android.bluetooth.IBluetoothManager; 35 import android.bluetooth.IBluetoothManagerCallback; 36 import android.content.AttributionSource; 37 import android.content.Context; 38 import android.os.Build; 39 import android.os.IBinder; 40 import android.os.Looper; 41 import android.os.ParcelFileDescriptor; 42 import android.os.UserManager; 43 import android.permission.PermissionManager; 44 45 import androidx.annotation.RequiresApi; 46 47 import java.io.FileDescriptor; 48 import java.io.PrintWriter; 49 50 class BluetoothServiceBinder extends IBluetoothManager.Stub { 51 private static final String TAG = BluetoothServiceBinder.class.getSimpleName(); 52 53 private final BluetoothManagerService mBluetoothManagerService; 54 private final Context mContext; 55 private final UserManager mUserManager; 56 private final AppOpsManager mAppOpsManager; 57 private final PermissionManager mPermissionManager; 58 private final BtPermissionUtils mPermissionUtils; 59 private final Looper unusedmLooper; 60 BluetoothServiceBinder( BluetoothManagerService bms, Looper looper, Context ctx, UserManager userManager)61 BluetoothServiceBinder( 62 BluetoothManagerService bms, Looper looper, Context ctx, UserManager userManager) { 63 mBluetoothManagerService = bms; 64 unusedmLooper = looper; 65 mContext = ctx; 66 mUserManager = userManager; 67 mAppOpsManager = 68 requireNonNull( 69 ctx.getSystemService(AppOpsManager.class), 70 "AppOpsManager system service cannot be null"); 71 mPermissionManager = 72 requireNonNull( 73 ctx.getSystemService(PermissionManager.class), 74 "PermissionManager system service cannot be null"); 75 mPermissionUtils = new BtPermissionUtils(ctx); 76 } 77 78 @Override 79 @Nullable registerAdapter(@onNull IBluetoothManagerCallback callback)80 public IBinder registerAdapter(@NonNull IBluetoothManagerCallback callback) { 81 requireNonNull(callback, "Callback cannot be null in registerAdapter"); 82 IBluetooth bluetooth = mBluetoothManagerService.registerAdapter(callback); 83 if (bluetooth == null) { 84 return null; 85 } 86 return bluetooth.asBinder(); 87 } 88 89 @Override unregisterAdapter(@onNull IBluetoothManagerCallback callback)90 public void unregisterAdapter(@NonNull IBluetoothManagerCallback callback) { 91 requireNonNull(callback, "Callback cannot be null in unregisterAdapter"); 92 mBluetoothManagerService.unregisterAdapter(callback); 93 } 94 95 @Override enable(@onNull AttributionSource source)96 public boolean enable(@NonNull AttributionSource source) { 97 requireNonNull(source, "AttributionSource cannot be null in enable"); 98 99 final String errorMsg = 100 mPermissionUtils.callerCanToggle( 101 mContext, 102 source, 103 mUserManager, 104 mAppOpsManager, 105 mPermissionManager, 106 "enable", 107 true); 108 if (!errorMsg.isEmpty()) { 109 Log.d(TAG, "enable(): FAILED: " + errorMsg); 110 return false; 111 } 112 113 Log.d(TAG, "enable()"); 114 return mBluetoothManagerService.enableFromBinder(source.getPackageName()); 115 } 116 117 @Override enableNoAutoConnect(AttributionSource source)118 public boolean enableNoAutoConnect(AttributionSource source) { 119 requireNonNull(source, "AttributionSource cannot be null in enableNoAutoConnect"); 120 121 final String errorMsg = 122 mPermissionUtils.callerCanToggle( 123 mContext, 124 source, 125 mUserManager, 126 mAppOpsManager, 127 mPermissionManager, 128 "enableNoAutoConnect", 129 false); 130 if (!errorMsg.isEmpty()) { 131 Log.d(TAG, "enableNoAutoConnect(): FAILED: " + errorMsg); 132 return false; 133 } 134 135 if (!BtPermissionUtils.isCallerNfc(getCallingAppId())) { 136 throw new SecurityException("No permission to enable Bluetooth quietly"); 137 } 138 139 Log.d(TAG, "enableNoAutoConnect()"); 140 return mBluetoothManagerService.enableNoAutoConnectFromBinder(source.getPackageName()); 141 } 142 143 @Override disable(AttributionSource source, boolean persist)144 public boolean disable(AttributionSource source, boolean persist) { 145 requireNonNull(source, "AttributionSource cannot be null in disable"); 146 147 if (!persist) { 148 BtPermissionUtils.enforcePrivileged(mContext); 149 } 150 151 final String errorMsg = 152 mPermissionUtils.callerCanToggle( 153 mContext, 154 source, 155 mUserManager, 156 mAppOpsManager, 157 mPermissionManager, 158 "disable", 159 true); 160 if (!errorMsg.isEmpty()) { 161 Log.d(TAG, "disable(): FAILED: " + errorMsg); 162 return false; 163 } 164 165 Log.d(TAG, "disable(" + persist + ")"); 166 return mBluetoothManagerService.disableFromBinder(source.getPackageName(), persist); 167 } 168 169 @Override getState()170 public int getState() { 171 return mBluetoothManagerService.getState(); 172 } 173 174 @Override getAddress(AttributionSource source)175 public String getAddress(AttributionSource source) { 176 requireNonNull(source, "AttributionSource cannot be null in getAddress"); 177 178 if (!checkConnectPermissionForDataDelivery( 179 mContext, mPermissionManager, source, "getAddress")) { 180 return null; 181 } 182 183 if (!isCallerSystem(getCallingAppId()) 184 && !mPermissionUtils.checkIfCallerIsForegroundUser(mUserManager)) { 185 Log.w(TAG, "getAddress(): Not allowed for non-active and non system user"); 186 return null; 187 } 188 189 if (mContext.checkCallingOrSelfPermission(LOCAL_MAC_ADDRESS) != PERMISSION_GRANTED) { 190 // TODO(b/280890575): Throws a SecurityException instead 191 Log.w(TAG, "getAddress(): Client does not have LOCAL_MAC_ADDRESS permission"); 192 return BluetoothAdapter.DEFAULT_MAC_ADDRESS; 193 } 194 195 return mBluetoothManagerService.getAddress(); 196 } 197 198 @Override getName(AttributionSource source)199 public String getName(AttributionSource source) { 200 requireNonNull(source, "AttributionSource cannot be null in getName"); 201 202 if (!checkConnectPermissionForDataDelivery( 203 mContext, mPermissionManager, source, "getName")) { 204 return null; 205 } 206 207 if (!isCallerSystem(getCallingAppId()) 208 && !mPermissionUtils.checkIfCallerIsForegroundUser(mUserManager)) { 209 Log.w(TAG, "getName(): not allowed for non-active and non system user"); 210 return null; 211 } 212 213 return mBluetoothManagerService.getName(); 214 } 215 216 @Override onFactoryReset(AttributionSource source)217 public boolean onFactoryReset(AttributionSource source) { 218 requireNonNull(source, "AttributionSource cannot be null in onFactoryReset"); 219 220 BtPermissionUtils.enforcePrivileged(mContext); 221 222 if (!checkConnectPermissionForDataDelivery( 223 mContext, mPermissionManager, source, "onFactoryReset")) { 224 return false; 225 } 226 227 return mBluetoothManagerService.onFactoryResetFromBinder(); 228 } 229 230 @Override isBleScanAvailable()231 public boolean isBleScanAvailable() { 232 return mBluetoothManagerService.isBleScanAvailable(); 233 } 234 235 @Override enableBle(AttributionSource source, IBinder token)236 public boolean enableBle(AttributionSource source, IBinder token) { 237 requireNonNull(source, "AttributionSource cannot be null in enableBle"); 238 requireNonNull(token, "IBinder cannot be null in enableBle"); 239 240 final String errorMsg = 241 mPermissionUtils.callerCanToggle( 242 mContext, 243 source, 244 mUserManager, 245 mAppOpsManager, 246 mPermissionManager, 247 "enableBle", 248 false); 249 if (!errorMsg.isEmpty()) { 250 Log.d(TAG, "enableBle(): FAILED: " + errorMsg); 251 return false; 252 } 253 254 Log.d(TAG, "enableBle(" + token + ")"); 255 return mBluetoothManagerService.enableBleFromBinder(source.getPackageName(), token); 256 } 257 258 @Override disableBle(AttributionSource source, IBinder token)259 public boolean disableBle(AttributionSource source, IBinder token) { 260 requireNonNull(source, "AttributionSource cannot be null in disableBle"); 261 requireNonNull(token, "IBinder cannot be null in disableBle"); 262 263 final String errorMsg = 264 mPermissionUtils.callerCanToggle( 265 mContext, 266 source, 267 mUserManager, 268 mAppOpsManager, 269 mPermissionManager, 270 "disableBle", 271 false); 272 if (!errorMsg.isEmpty()) { 273 Log.d(TAG, "disableBle(): FAILED: " + errorMsg); 274 return false; 275 } 276 277 Log.d(TAG, "disableBle(" + token + ")"); 278 return mBluetoothManagerService.disableBleFromBinder(source.getPackageName(), token); 279 } 280 281 @Override isHearingAidProfileSupported()282 public boolean isHearingAidProfileSupported() { 283 return mBluetoothManagerService.isHearingAidProfileSupported(); 284 } 285 286 @Override setBtHciSnoopLogMode(int mode)287 public int setBtHciSnoopLogMode(int mode) { 288 BtPermissionUtils.enforcePrivileged(mContext); 289 290 return mBluetoothManagerService.setBtHciSnoopLogMode(mode); 291 } 292 293 @Override getBtHciSnoopLogMode()294 public int getBtHciSnoopLogMode() { 295 BtPermissionUtils.enforcePrivileged(mContext); 296 297 return mBluetoothManagerService.getBtHciSnoopLogMode(); 298 } 299 300 @Override handleShellCommand( @onNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args)301 public int handleShellCommand( 302 @NonNull ParcelFileDescriptor in, 303 @NonNull ParcelFileDescriptor out, 304 @NonNull ParcelFileDescriptor err, 305 @NonNull String[] args) { 306 return new BluetoothShellCommand(mBluetoothManagerService) 307 .exec( 308 this, 309 in.getFileDescriptor(), 310 out.getFileDescriptor(), 311 err.getFileDescriptor(), 312 args); 313 } 314 315 @Override isAutoOnSupported()316 public boolean isAutoOnSupported() { 317 BtPermissionUtils.enforcePrivileged(mContext); 318 return mBluetoothManagerService.isAutoOnSupported(); 319 } 320 321 @Override isAutoOnEnabled()322 public boolean isAutoOnEnabled() { 323 BtPermissionUtils.enforcePrivileged(mContext); 324 return mBluetoothManagerService.isAutoOnEnabled(); 325 } 326 327 @Override 328 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) setAutoOnEnabled(boolean status)329 public void setAutoOnEnabled(boolean status) { 330 BtPermissionUtils.enforcePrivileged(mContext); 331 mBluetoothManagerService.setAutoOnEnabled(status); 332 } 333 334 @Override dump(FileDescriptor fd, PrintWriter writer, String[] args)335 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 336 if (mContext.checkCallingOrSelfPermission(DUMP) != PERMISSION_GRANTED) { 337 // TODO(b/280890575): Throws SecurityException instead 338 Log.w(TAG, "dump(): Client does not have DUMP permission"); 339 return; 340 } 341 342 mBluetoothManagerService.dump(fd, writer, args); 343 } 344 } 345