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