diff options
Diffstat (limited to 'framework/java/android/bluetooth/BluetoothAdapter.java')
| -rw-r--r-- | framework/java/android/bluetooth/BluetoothAdapter.java | 966 |
1 files changed, 603 insertions, 363 deletions
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e305aa8e33..a9ddefbaea 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -19,19 +19,26 @@ package android.bluetooth; import static java.util.Objects.requireNonNull; -import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.ActivityThread; import android.app.PropertyInvalidatedCache; import android.bluetooth.BluetoothDevice.Transport; import android.bluetooth.BluetoothProfile.ConnectionPolicy; +import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresBluetoothLocationPermission; +import android.bluetooth.annotations.RequiresBluetoothScanPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.PeriodicAdvertisingManager; @@ -41,6 +48,8 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; +import android.content.AttributionSource; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; @@ -66,6 +75,7 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; @@ -96,11 +106,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. * </p> * <p>This class is thread safe.</p> - * <p class="note"><strong>Note:</strong> - * Most methods require the {@link android.Manifest.permission#BLUETOOTH} - * permission and some also require the - * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * </p> * <div class="special reference"> * <h3>Developer Guides</h3> * <p> @@ -142,8 +147,8 @@ public final class BluetoothAdapter { * <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link * #EXTRA_PREVIOUS_STATE} containing the new and old states * respectively. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED"; @@ -276,8 +281,10 @@ public final class BluetoothAdapter { * <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED} * for global notification whenever the scan mode changes. For example, an * application can be notified when the device has ended discoverability. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"; @@ -303,8 +310,10 @@ public final class BluetoothAdapter { * has rejected the request or an error has occurred. * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED} * for global notification whenever Bluetooth is turned on or off. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; @@ -323,10 +332,12 @@ public final class BluetoothAdapter { * has rejected the request or an error has occurred. * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED} * for global notification whenever Bluetooth is turned on or off. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} * * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE"; @@ -353,8 +364,10 @@ public final class BluetoothAdapter { * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes * respectively. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; @@ -506,15 +519,19 @@ public final class BluetoothAdapter { * progress, and existing connections will experience limited bandwidth * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing * discovery. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED"; /** * Broadcast Action: The local Bluetooth adapter has finished the device * discovery process. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; @@ -524,8 +541,10 @@ public final class BluetoothAdapter { * <p>This name is visible to remote Bluetooth devices. * <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing * the name. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED"; /** @@ -557,9 +576,10 @@ public final class BluetoothAdapter { * {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE} * can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"; @@ -585,6 +605,7 @@ public final class BluetoothAdapter { * * @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @SystemApi public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; @@ -599,6 +620,9 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED = "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED"; @@ -623,6 +647,9 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BLE_ACL_CONNECTED = "android.bluetooth.adapter.action.BLE_ACL_CONNECTED"; @@ -637,6 +664,9 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BLE_ACL_DISCONNECTED = "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; @@ -677,20 +707,21 @@ public final class BluetoothAdapter { */ private static BluetoothAdapter sAdapter; - private static BluetoothLeScanner sBluetoothLeScanner; - private static BluetoothLeAdvertiser sBluetoothLeAdvertiser; - private static PeriodicAdvertisingManager sPeriodicAdvertisingManager; + private BluetoothLeScanner mBluetoothLeScanner; + private BluetoothLeAdvertiser mBluetoothLeAdvertiser; + private PeriodicAdvertisingManager mPeriodicAdvertisingManager; private final IBluetoothManager mManagerService; + private final AttributionSource mAttributionSource; + @UnsupportedAppUsage private IBluetooth mService; - private Context mContext; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); private final Object mLock = new Object(); private final Map<LeScanCallback, ScanCallback> mLeScanClients; - private static final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>> - sMetadataListeners = new HashMap<>(); + private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>> + mMetadataListeners = new HashMap<>(); private final Map<BluetoothConnectionCallback, Executor> mBluetoothConnectionCallbackExecutorMap = new HashMap<>(); @@ -698,14 +729,16 @@ public final class BluetoothAdapter { * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener * implementation. */ - private static final IBluetoothMetadataListener sBluetoothMetadataListener = + @SuppressLint("AndroidFrameworkBluetoothPermission") + private final IBluetoothMetadataListener mBluetoothMetadataListener = new IBluetoothMetadataListener.Stub() { @Override public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) { - synchronized (sMetadataListeners) { - if (sMetadataListeners.containsKey(device)) { + Attributable.setAttributionSource(device, mAttributionSource); + synchronized (mMetadataListeners) { + if (mMetadataListeners.containsKey(device)) { List<Pair<OnMetadataChangedListener, Executor>> list = - sMetadataListeners.get(device); + mMetadataListeners.get(device); for (Pair<OnMetadataChangedListener, Executor> pair : list) { OnMetadataChangedListener listener = pair.first; Executor executor = pair.second; @@ -721,32 +754,43 @@ public final class BluetoothAdapter { /** * Get a handle to the default local Bluetooth adapter. - * <p>Currently Android only supports one Bluetooth adapter, but the API - * could be extended to support more. This will always return the default - * adapter. + * <p> + * Currently Android only supports one Bluetooth adapter, but the API could + * be extended to support more. This will always return the default adapter. * </p> * - * @return the default local adapter, or null if Bluetooth is not supported on this hardware - * platform + * @return the default local adapter, or null if Bluetooth is not supported + * on this hardware platform + * @deprecated this method will continue to work, but developers are + * strongly encouraged to migrate to using + * {@link BluetoothManager#getAdapter()}, since that approach + * enables support for {@link Context#createAttributionContext}. */ + @Deprecated + @RequiresNoPermission public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { - IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b); - sAdapter = new BluetoothAdapter(managerService); - } else { - Log.e(TAG, "Bluetooth binder is null"); - } + sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null)); } return sAdapter; } + /** {@hide} */ + public static BluetoothAdapter createAdapter(AttributionSource attributionSource) { + IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); + if (binder != null) { + return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder), + attributionSource); + } else { + Log.e(TAG, "Bluetooth binder is null"); + return null; + } + } + /** * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ - BluetoothAdapter(IBluetoothManager managerService) { - + BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) { if (managerService == null) { throw new IllegalArgumentException("bluetooth manager service is null"); } @@ -758,7 +802,8 @@ public final class BluetoothAdapter { } finally { mServiceLock.writeLock().unlock(); } - mManagerService = managerService; + mManagerService = Objects.requireNonNull(managerService); + mAttributionSource = Objects.requireNonNull(attributionSource); mLeScanClients = new HashMap<LeScanCallback, ScanCallback>(); mToken = new Binder(); } @@ -775,8 +820,11 @@ public final class BluetoothAdapter { * @param address valid Bluetooth MAC address * @throws IllegalArgumentException if address is invalid */ + @RequiresNoPermission public BluetoothDevice getRemoteDevice(String address) { - return new BluetoothDevice(address); + final BluetoothDevice res = new BluetoothDevice(address); + res.setAttributionSource(mAttributionSource); + return res; } /** @@ -790,13 +838,16 @@ public final class BluetoothAdapter { * @param address Bluetooth MAC address (6 bytes) * @throws IllegalArgumentException if address is invalid */ + @RequiresNoPermission public BluetoothDevice getRemoteDevice(byte[] address) { if (address == null || address.length != 6) { throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); } - return new BluetoothDevice( + final BluetoothDevice res = new BluetoothDevice( String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], address[2], address[3], address[4], address[5])); + res.setAttributionSource(mAttributionSource); + return res; } /** @@ -807,16 +858,17 @@ public final class BluetoothAdapter { * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported * on this device before calling this method. */ + @RequiresNoPermission public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { if (!getLeAccess()) { return null; } synchronized (mLock) { - if (sBluetoothLeAdvertiser == null) { - sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService); + if (mBluetoothLeAdvertiser == null) { + mBluetoothLeAdvertiser = new BluetoothLeAdvertiser(this); } + return mBluetoothLeAdvertiser; } - return sBluetoothLeAdvertiser; } /** @@ -829,6 +881,7 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresNoPermission public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { if (!getLeAccess()) { return null; @@ -839,27 +892,27 @@ public final class BluetoothAdapter { } synchronized (mLock) { - if (sPeriodicAdvertisingManager == null) { - sPeriodicAdvertisingManager = new PeriodicAdvertisingManager(mManagerService); + if (mPeriodicAdvertisingManager == null) { + mPeriodicAdvertisingManager = new PeriodicAdvertisingManager(this); } + return mPeriodicAdvertisingManager; } - return sPeriodicAdvertisingManager; } /** * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. */ + @RequiresNoPermission public BluetoothLeScanner getBluetoothLeScanner() { if (!getLeAccess()) { return null; } synchronized (mLock) { - if (sBluetoothLeScanner == null) { - sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(), - getAttributionTag()); + if (mBluetoothLeScanner == null) { + mBluetoothLeScanner = new BluetoothLeScanner(this); } + return mBluetoothLeScanner; } - return sBluetoothLeScanner; } /** @@ -869,7 +922,8 @@ public final class BluetoothAdapter { * * @return true if the local adapter is turned on */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isEnabled() { return getState() == BluetoothAdapter.STATE_ON; } @@ -883,6 +937,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresNoPermission public boolean isLeEnabled() { final int state = getLeState(); if (DBG) { @@ -920,13 +975,15 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disableBLE() { if (!isBleScanAlwaysAvailable()) { return false; } String packageName = ActivityThread.currentPackageName(); try { - return mManagerService.disableBle(packageName, mToken); + return mManagerService.disableBle(mAttributionSource, mToken); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -965,13 +1022,15 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enableBLE() { if (!isBleScanAlwaysAvailable()) { return false; } String packageName = ActivityThread.currentPackageName(); try { - return mManagerService.enableBle(packageName, mToken); + return mManagerService.enableBle(mAttributionSource, mToken); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -985,6 +1044,7 @@ public final class BluetoothAdapter { new PropertyInvalidatedCache<Void, Integer>( 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { @Override + @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(Void query) { try { return mService.getState(); @@ -995,6 +1055,7 @@ public final class BluetoothAdapter { }; /** @hide */ + @RequiresNoPermission public void disableBluetoothGetStateCache() { mBluetoothGetStateCache.disableLocal(); } @@ -1038,7 +1099,8 @@ public final class BluetoothAdapter { * * @return current state of Bluetooth adapter */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresNoPermission @AdapterState public int getState() { int state = getStateInternal(); @@ -1074,7 +1136,8 @@ public final class BluetoothAdapter { * @return current state of Bluetooth adapter * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresNoPermission @AdapterState @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine " + "whether you can use BLE & BT classic.") @@ -1121,7 +1184,9 @@ public final class BluetoothAdapter { * * @return true to indicate adapter startup has begun, or false on immediate error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enable() { if (isEnabled()) { if (DBG) { @@ -1130,7 +1195,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enable(ActivityThread.currentPackageName()); + return mManagerService.enable(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1158,10 +1223,12 @@ public final class BluetoothAdapter { * * @return true to indicate adapter shutdown has begun, or false on immediate error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disable() { try { - return mManagerService.disable(ActivityThread.currentPackageName(), true); + return mManagerService.disable(mAttributionSource, true); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1171,17 +1238,17 @@ public final class BluetoothAdapter { /** * Turn off the local Bluetooth adapter and don't persist the setting. * - * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission - * * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ @UnsupportedAppUsage(trackingBug = 171933273) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disable(boolean persist) { try { - return mManagerService.disable(ActivityThread.currentPackageName(), persist); + return mManagerService.disable(mAttributionSource, persist); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1194,10 +1261,15 @@ public final class BluetoothAdapter { * * @return Bluetooth hardware address as string */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.LOCAL_MAC_ADDRESS, + }) public String getAddress() { try { - return mManagerService.getAddress(); + return mManagerService.getAddress(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1207,19 +1279,33 @@ public final class BluetoothAdapter { /** * Get the friendly Bluetooth name of the local Bluetooth adapter. * <p>This name is visible to remote Bluetooth devices. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} * * @return the Bluetooth name, or null on error */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName() { try { - return mManagerService.getName(); + return mManagerService.getName(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } return null; } + /** {@hide} */ + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) + public int getNameLengthForAdvertise() { + try { + return mService.getNameLengthForAdvertise(mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return -1; + } + /** * Factory reset bluetooth settings. * @@ -1227,12 +1313,17 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean factoryReset() { try { mServiceLock.readLock().lock(); - if (mService != null && mService.factoryReset() - && mManagerService != null && mManagerService.onFactoryReset()) { + if (mService != null && mService.factoryReset(mAttributionSource) + && mManagerService != null + && mManagerService.onFactoryReset(mAttributionSource)) { return true; } Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); @@ -1252,7 +1343,9 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @Nullable ParcelUuid[] getUuids() { if (getState() != STATE_ON) { return null; @@ -1260,7 +1353,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getUuids(); + return mService.getUuids(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1284,7 +1377,9 @@ public final class BluetoothAdapter { * @param name a valid Bluetooth name * @return true if the name was set, false otherwise */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setName(String name) { if (getState() != STATE_ON) { return false; @@ -1292,7 +1387,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setName(name); + return mService.setName(name, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1310,7 +1405,9 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothClass getBluetoothClass() { if (getState() != STATE_ON) { return null; @@ -1318,7 +1415,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getBluetoothClass(); + return mService.getBluetoothClass(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1339,7 +1436,11 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setBluetoothClass(BluetoothClass bluetoothClass) { if (getState() != STATE_ON) { return false; @@ -1347,7 +1448,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setBluetoothClass(bluetoothClass); + return mService.setBluetoothClass(bluetoothClass, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1366,13 +1467,15 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @IoCapability public int getIoCapability() { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getIoCapability(); + if (mService != null) return mService.getIoCapability(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1394,12 +1497,16 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setIoCapability(@IoCapability int capability) { if (getState() != STATE_ON) return false; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.setIoCapability(capability); + if (mService != null) return mService.setIoCapability(capability, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1417,13 +1524,15 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @IoCapability public int getLeIoCapability() { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getLeIoCapability(); + if (mService != null) return mService.getLeIoCapability(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1445,12 +1554,16 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setLeIoCapability(@IoCapability int capability) { if (getState() != STATE_ON) return false; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.setLeIoCapability(capability); + if (mService != null) return mService.setLeIoCapability(capability, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1474,7 +1587,9 @@ public final class BluetoothAdapter { * * @return scan mode */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @ScanMode public int getScanMode() { if (getState() != STATE_ON) { @@ -1483,7 +1598,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getScanMode(); + return mService.getScanMode(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1521,7 +1636,9 @@ public final class BluetoothAdapter { */ @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which " + "shows UI that confirms the user wants to go into discoverable mode.") - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean setScanMode(@ScanMode int mode, long durationMillis) { if (getState() != STATE_ON) { return false; @@ -1530,7 +1647,7 @@ public final class BluetoothAdapter { mServiceLock.readLock().lock(); if (mService != null) { int durationSeconds = Math.toIntExact(durationMillis / 1000); - return mService.setScanMode(mode, durationSeconds); + return mService.setScanMode(mode, durationSeconds, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1570,7 +1687,9 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean setScanMode(@ScanMode int mode) { if (getState() != STATE_ON) { return false; @@ -1578,7 +1697,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setScanMode(mode, getDiscoverableTimeout()); + return mService.setScanMode(mode, getDiscoverableTimeout(), mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1590,6 +1709,8 @@ public final class BluetoothAdapter { /** @hide */ @UnsupportedAppUsage + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public int getDiscoverableTimeout() { if (getState() != STATE_ON) { return -1; @@ -1597,7 +1718,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getDiscoverableTimeout(); + return mService.getDiscoverableTimeout(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1609,6 +1730,8 @@ public final class BluetoothAdapter { /** @hide */ @UnsupportedAppUsage + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void setDiscoverableTimeout(int timeout) { if (getState() != STATE_ON) { return; @@ -1616,7 +1739,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - mService.setDiscoverableTimeout(timeout); + mService.setDiscoverableTimeout(timeout, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1634,12 +1757,16 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public long getDiscoveryEndMillis() { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getDiscoveryEndMillis(); + return mService.getDiscoveryEndMillis(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1650,32 +1777,6 @@ public final class BluetoothAdapter { } /** - * Set the context for this BluetoothAdapter (only called from BluetoothManager) - * @hide - */ - public void setContext(Context context) { - mContext = context; - } - - private String getOpPackageName() { - // Workaround for legacy API for getting a BluetoothAdapter not - // passing a context - if (mContext != null) { - return mContext.getOpPackageName(); - } - return ActivityThread.currentOpPackageName(); - } - - private String getAttributionTag() { - // Workaround for legacy API for getting a BluetoothAdapter not - // passing a context - if (mContext != null) { - return mContext.getAttributionTag(); - } - return null; - } - - /** * Start the remote device discovery process. * <p>The discovery process usually involves an inquiry scan of about 12 * seconds, followed by a page scan of each new device to retrieve its @@ -1704,7 +1805,10 @@ public final class BluetoothAdapter { * * @return true on success, false on error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startDiscovery() { if (getState() != STATE_ON) { return false; @@ -1712,7 +1816,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(getOpPackageName(), getAttributionTag()); + return mService.startDiscovery(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1738,7 +1842,9 @@ public final class BluetoothAdapter { * * @return true on success, false on error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean cancelDiscovery() { if (getState() != STATE_ON) { return false; @@ -1746,7 +1852,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.cancelDiscovery(); + return mService.cancelDiscovery(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1774,7 +1880,9 @@ public final class BluetoothAdapter { * * @return true if discovering */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isDiscovering() { if (getState() != STATE_ON) { return false; @@ -1782,7 +1890,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.isDiscovering(); + return mService.isDiscovering(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1806,7 +1914,12 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public boolean removeActiveDevice(@ActiveDeviceUse int profiles) { if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL && profiles != ACTIVE_DEVICE_ALL) { @@ -1820,7 +1933,7 @@ public final class BluetoothAdapter { mServiceLock.readLock().lock(); if (mService != null) { if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles); - return mService.removeActiveDevice(profiles); + return mService.removeActiveDevice(profiles, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1846,7 +1959,12 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public boolean setActiveDevice(@NonNull BluetoothDevice device, @ActiveDeviceUse int profiles) { if (device == null) { @@ -1867,7 +1985,7 @@ public final class BluetoothAdapter { if (DBG) { Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles); } - return mService.setActiveDevice(device, profiles); + return mService.setActiveDevice(device, profiles, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1890,12 +2008,17 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.connectAllEnabledProfiles(device); + return mService.connectAllEnabledProfiles(device, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1918,12 +2041,16 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.disconnectAllEnabledProfiles(device); + return mService.disconnectAllEnabledProfiles(device, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1939,6 +2066,8 @@ public final class BluetoothAdapter { * * @return true if Multiple Advertisement feature is supported */ + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isMultipleAdvertisementSupported() { if (getState() != STATE_ON) { return false; @@ -1967,6 +2096,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresNoPermission public boolean isBleScanAlwaysAvailable() { try { return mManagerService.isBleScanAlwaysAvailable(); @@ -1982,6 +2112,7 @@ public final class BluetoothAdapter { new PropertyInvalidatedCache<Void, Boolean>( 8, BLUETOOTH_FILTERING_CACHE_PROPERTY) { @Override + @SuppressLint("AndroidFrameworkRequiresPermission") protected Boolean recompute(Void query) { try { mServiceLock.readLock().lock(); @@ -1999,6 +2130,7 @@ public final class BluetoothAdapter { }; /** @hide */ + @RequiresNoPermission public void disableIsOffloadedFilteringSupportedCache() { mBluetoothFilteringCache.disableLocal(); } @@ -2013,6 +2145,8 @@ public final class BluetoothAdapter { * * @return true if chipset supports on-chip filtering */ + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isOffloadedFilteringSupported() { if (!getLeAccess()) { return false; @@ -2025,6 +2159,8 @@ public final class BluetoothAdapter { * * @return true if chipset supports on-chip scan batching */ + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isOffloadedScanBatchingSupported() { if (!getLeAccess()) { return false; @@ -2047,6 +2183,8 @@ public final class BluetoothAdapter { * * @return true if chipset supports LE 2M PHY feature */ + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isLe2MPhySupported() { if (!getLeAccess()) { return false; @@ -2069,6 +2207,8 @@ public final class BluetoothAdapter { * * @return true if chipset supports LE Coded PHY feature */ + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isLeCodedPhySupported() { if (!getLeAccess()) { return false; @@ -2091,6 +2231,8 @@ public final class BluetoothAdapter { * * @return true if chipset supports LE Extended Advertising feature */ + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isLeExtendedAdvertisingSupported() { if (!getLeAccess()) { return false; @@ -2113,6 +2255,8 @@ public final class BluetoothAdapter { * * @return true if chipset supports LE Periodic Advertising feature */ + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isLePeriodicAdvertisingSupported() { if (!getLeAccess()) { return false; @@ -2136,6 +2280,8 @@ public final class BluetoothAdapter { * * @return the maximum LE advertising data length. */ + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public int getLeMaximumAdvertisingDataLength() { if (!getLeAccess()) { return 0; @@ -2158,6 +2304,7 @@ public final class BluetoothAdapter { * * @return true if phone supports Hearing Aid Profile */ + @RequiresNoPermission private boolean isHearingAidProfileSupported() { try { return mManagerService.isHearingAidProfileSupported(); @@ -2173,12 +2320,14 @@ public final class BluetoothAdapter { * @return the maximum number of connected audio devices * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getMaxConnectedAudioDevices() { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMaxConnectedAudioDevices(); + return mService.getMaxConnectedAudioDevices(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e); @@ -2194,6 +2343,8 @@ public final class BluetoothAdapter { * @return true if there are hw entries available for matching beacons * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isHardwareTrackingFiltersAvailable() { if (!getLeAccess()) { return false; @@ -2204,7 +2355,7 @@ public final class BluetoothAdapter { // BLE is not supported return false; } - return (iGatt.numHwTrackFiltersAvailable() != 0); + return (iGatt.numHwTrackFiltersAvailable(mAttributionSource) != 0); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2224,6 +2375,11 @@ public final class BluetoothAdapter { * instead. */ @Deprecated + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { SynchronousResultReceiver receiver = new SynchronousResultReceiver(); requestControllerActivityEnergyInfo(receiver); @@ -2249,11 +2405,16 @@ public final class BluetoothAdapter { * @param result The callback to which to send the activity info. * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public void requestControllerActivityEnergyInfo(ResultReceiver result) { try { mServiceLock.readLock().lock(); if (mService != null) { - mService.requestActivityInfo(result); + mService.requestActivityInfo(result, mAttributionSource); result = null; } } catch (RemoteException e) { @@ -2276,7 +2437,9 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List<BluetoothDevice> getMostRecentlyConnectedDevices() { if (getState() != STATE_ON) { return new ArrayList<>(); @@ -2284,7 +2447,9 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMostRecentlyConnectedDevices(); + return Attributable.setAttributionSource( + mService.getMostRecentlyConnectedDevices(mAttributionSource), + mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2304,17 +2469,21 @@ public final class BluetoothAdapter { * * @return unmodifiable set of {@link BluetoothDevice}, or null on error */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public Set<BluetoothDevice> getBondedDevices() { if (getState() != STATE_ON) { - return toDeviceSet(new BluetoothDevice[0]); + return toDeviceSet(Arrays.asList()); } try { mServiceLock.readLock().lock(); if (mService != null) { - return toDeviceSet(mService.getBondedDevices()); + return toDeviceSet(Attributable.setAttributionSource( + Arrays.asList(mService.getBondedDevices(mAttributionSource)), + mAttributionSource)); } - return toDeviceSet(new BluetoothDevice[0]); + return toDeviceSet(Arrays.asList()); } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -2333,6 +2502,7 @@ public final class BluetoothAdapter { * BluetoothProfile}. * @hide */ + @RequiresNoPermission public @NonNull List<Integer> getSupportedProfiles() { final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>(); @@ -2359,6 +2529,38 @@ public final class BluetoothAdapter { return supportedProfiles; } + private static final String BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY = + "cache_key.bluetooth.get_adapter_connection_state"; + private final PropertyInvalidatedCache<Void, Integer> + mBluetoothGetAdapterConnectionStateCache = + new PropertyInvalidatedCache<Void, Integer> ( + 8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) { + /** + * This method must not be called when mService is null. + */ + @Override + @SuppressLint("AndroidFrameworkRequiresPermission") + protected Integer recompute(Void query) { + try { + return mService.getAdapterConnectionState(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + }; + + /** @hide */ + @RequiresNoPermission + public void disableGetAdapterConnectionStateCache() { + mBluetoothGetAdapterConnectionStateCache.disableLocal(); + } + + /** @hide */ + public static void invalidateGetAdapterConnectionStateCache() { + PropertyInvalidatedCache.invalidateCache( + BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY); + } + /** * Get the current connection state of the local Bluetooth adapter. * This can be used to check whether the local Bluetooth adapter is connected @@ -2372,6 +2574,8 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage + @RequiresLegacyBluetoothPermission + @RequiresNoPermission public int getConnectionState() { if (getState() != STATE_ON) { return BluetoothAdapter.STATE_DISCONNECTED; @@ -2379,10 +2583,14 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getAdapterConnectionState(); + return mBluetoothGetAdapterConnectionStateCache.query(null); + } + } catch (RuntimeException e) { + if (e.getCause() instanceof RemoteException) { + Log.e(TAG, "getConnectionState:", e.getCause()); + } else { + throw e; } - } catch (RemoteException e) { - Log.e(TAG, "getConnectionState:", e); } finally { mServiceLock.readLock().unlock(); } @@ -2396,6 +2604,7 @@ public final class BluetoothAdapter { new PropertyInvalidatedCache<Integer, Integer>( 8, BLUETOOTH_PROFILE_CACHE_PROPERTY) { @Override + @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(Integer query) { try { mServiceLock.readLock().lock(); @@ -2417,6 +2626,7 @@ public final class BluetoothAdapter { }; /** @hide */ + @RequiresNoPermission public void disableGetProfileConnectionStateCache() { mGetProfileConnectionStateCache.disableLocal(); } @@ -2438,7 +2648,10 @@ public final class BluetoothAdapter { * {@link BluetoothProfile#STATE_CONNECTED}, * {@link BluetoothProfile#STATE_DISCONNECTING} */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public int getProfileConnectionState(int profile) { if (getState() != STATE_ON) { return BluetoothProfile.STATE_DISCONNECTED; @@ -2453,7 +2666,6 @@ public final class BluetoothAdapter { * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming * connections from a listening {@link BluetoothServerSocket}. * <p>Valid RFCOMM channels are in range 1 to 30. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param channel RFCOMM channel to listen on * @return a listening RFCOMM BluetoothServerSocket @@ -2461,6 +2673,9 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { return listenUsingRfcommOn(channel, false, false); } @@ -2472,7 +2687,6 @@ public final class BluetoothAdapter { * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming * connections from a listening {@link BluetoothServerSocket}. * <p>Valid RFCOMM channels are in range 1 to 30. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * <p>To auto assign a channel without creating a SDP record use * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. * @@ -2486,6 +2700,9 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm, boolean min16DigitPin) throws IOException { BluetoothServerSocket socket = @@ -2526,7 +2743,9 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions, or channel in use. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, true, true); @@ -2558,7 +2777,9 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions, or channel in use. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, false); @@ -2589,7 +2810,6 @@ public final class BluetoothAdapter { * closed, or if this application closes unexpectedly. * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to * connect to this socket from another device using the same {@link UUID}. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} * * @param name service name for SDP record * @param uuid uuid for SDP record @@ -2599,12 +2819,16 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, true); } - + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, boolean auth, boolean encrypt) throws IOException { BluetoothServerSocket socket; @@ -2630,6 +2854,8 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port); @@ -2661,6 +2887,8 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin) throws IOException { BluetoothServerSocket socket = @@ -2693,11 +2921,12 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { return listenUsingL2capOn(port, false, false); } - /** * Construct an insecure L2CAP server socket. * Call #accept to retrieve connections to this socket. @@ -2710,6 +2939,8 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port); BluetoothServerSocket socket = @@ -2736,11 +2967,14 @@ public final class BluetoothAdapter { /** * Read the local Out of Band Pairing Data - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} * * @return Pair<byte[], byte[]> of Hash and Randomizer * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public Pair<byte[], byte[]> readOutOfBandData() { return null; } @@ -2761,6 +2995,10 @@ public final class BluetoothAdapter { * BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}. * @return true on success, false on error */ + @SuppressLint({ + "AndroidFrameworkRequiresPermission", + "AndroidFrameworkBluetoothPermission" + }) public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, int profile) { if (context == null || listener == null) { @@ -2768,53 +3006,57 @@ public final class BluetoothAdapter { } if (profile == BluetoothProfile.HEADSET) { - BluetoothHeadset headset = new BluetoothHeadset(context, listener); + BluetoothHeadset headset = new BluetoothHeadset(context, listener, this); return true; } else if (profile == BluetoothProfile.A2DP) { - BluetoothA2dp a2dp = new BluetoothA2dp(context, listener); + BluetoothA2dp a2dp = new BluetoothA2dp(context, listener, this); return true; } else if (profile == BluetoothProfile.A2DP_SINK) { - BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener); + BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener, this); return true; } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) { - BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener); + BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener, this); return true; } else if (profile == BluetoothProfile.HID_HOST) { - BluetoothHidHost iDev = new BluetoothHidHost(context, listener); + BluetoothHidHost iDev = new BluetoothHidHost(context, listener, this); return true; } else if (profile == BluetoothProfile.PAN) { - BluetoothPan pan = new BluetoothPan(context, listener); + BluetoothPan pan = new BluetoothPan(context, listener, this); return true; } else if (profile == BluetoothProfile.PBAP) { - BluetoothPbap pbap = new BluetoothPbap(context, listener); + BluetoothPbap pbap = new BluetoothPbap(context, listener, this); return true; } else if (profile == BluetoothProfile.HEALTH) { Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated"); return false; } else if (profile == BluetoothProfile.MAP) { - BluetoothMap map = new BluetoothMap(context, listener); + BluetoothMap map = new BluetoothMap(context, listener, this); return true; } else if (profile == BluetoothProfile.HEADSET_CLIENT) { - BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener); + BluetoothHeadsetClient headsetClient = + new BluetoothHeadsetClient(context, listener, this); return true; } else if (profile == BluetoothProfile.SAP) { - BluetoothSap sap = new BluetoothSap(context, listener); + BluetoothSap sap = new BluetoothSap(context, listener, this); return true; } else if (profile == BluetoothProfile.PBAP_CLIENT) { - BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener); + BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener, this); return true; } else if (profile == BluetoothProfile.MAP_CLIENT) { - BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); + BluetoothMapClient mapClient = new BluetoothMapClient(context, listener, this); return true; } else if (profile == BluetoothProfile.HID_DEVICE) { - BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener); + BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this); return true; } else if (profile == BluetoothProfile.HEARING_AID) { if (isHearingAidProfileSupported()) { - BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener); + BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this); return true; } return false; + } else if (profile == BluetoothProfile.LE_AUDIO) { + BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this); + return true; } else if (profile == BluetoothProfile.VOLUME_CONTROL) { BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); return true; @@ -2833,6 +3075,10 @@ public final class BluetoothAdapter { * @param profile * @param proxy Profile proxy object */ + @SuppressLint({ + "AndroidFrameworkRequiresPermission", + "AndroidFrameworkBluetoothPermission" + }) public void closeProfileProxy(int profile, BluetoothProfile proxy) { if (proxy == null) { return; @@ -2903,6 +3149,10 @@ public final class BluetoothAdapter { BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; hearingAid.close(); break; + case BluetoothProfile.LE_AUDIO: + BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy; + leAudio.close(); + break; case BluetoothProfile.VOLUME_CONTROL: BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy; vcs.close(); @@ -2910,8 +3160,10 @@ public final class BluetoothAdapter { } } + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothManagerCallback mManagerCallback = new IBluetoothManagerCallback.Stub() { + @SuppressLint("AndroidFrameworkRequiresPermission") public void onBluetoothServiceUp(IBluetooth bluetoothService) { if (DBG) { Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); @@ -2934,11 +3186,11 @@ public final class BluetoothAdapter { } } } - synchronized (sMetadataListeners) { - sMetadataListeners.forEach((device, pair) -> { + synchronized (mMetadataListeners) { + mMetadataListeners.forEach((device, pair) -> { try { - mService.registerMetadataListener(sBluetoothMetadataListener, - device); + mService.registerMetadataListener(mBluetoothMetadataListener, + device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to register metadata listener", e); } @@ -2947,7 +3199,8 @@ public final class BluetoothAdapter { synchronized (mBluetoothConnectionCallbackExecutorMap) { if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { try { - mService.registerBluetoothConnectionCallback(mConnectionCallback); + mService.registerBluetoothConnectionCallback(mConnectionCallback, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth" + "connection callback", e); @@ -2967,11 +3220,11 @@ public final class BluetoothAdapter { if (mLeScanClients != null) { mLeScanClients.clear(); } - if (sBluetoothLeAdvertiser != null) { - sBluetoothLeAdvertiser.cleanup(); + if (mBluetoothLeAdvertiser != null) { + mBluetoothLeAdvertiser.cleanup(); } - if (sBluetoothLeScanner != null) { - sBluetoothLeScanner.cleanup(); + if (mBluetoothLeScanner != null) { + mBluetoothLeScanner.cleanup(); } } finally { mServiceLock.writeLock().unlock(); @@ -3006,7 +3259,9 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enableNoAutoConnect() { if (isEnabled()) { if (DBG) { @@ -3015,7 +3270,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName()); + return mManagerService.enableNoAutoConnect(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -3024,41 +3279,16 @@ public final class BluetoothAdapter { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "OOB_ERROR_" }, value = { - OOB_ERROR_UNKNOWN, - OOB_ERROR_ANOTHER_ACTIVE_REQUEST, - OOB_ERROR_ADAPTER_DISABLED + @IntDef(value = { + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, + BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST, }) public @interface OobError {} /** - * An unknown error has occurred in the controller, stack, or callback pipeline. - * - * @hide - */ - @SystemApi - public static final int OOB_ERROR_UNKNOWN = 0; - - /** - * If another application has already requested {@link OobData} then another fetch will be - * disallowed until the callback is removed. - * - * @hide - */ - @SystemApi - public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1; - - /** - * The adapter is currently disabled, please enable it. - * - * @hide - */ - @SystemApi - public static final int OOB_ERROR_ADAPTER_DISABLED = 2; - - /** * Provides callback methods for receiving {@link OobData} from the host stack, as well as an - * error interface in order to allow the caller to determine next steps based on the {@link + * error interface in order to allow the caller to determine next steps based on the {@code * ErrorCode}. * * @hide @@ -3076,7 +3306,7 @@ public final class BluetoothAdapter { /** * Provides feedback when things don't go as expected. * - * @param errorCode - the code descibing the type of error that occurred. + * @param errorCode - the code describing the type of error that occurred. */ void onError(@OobError int errorCode); } @@ -3159,7 +3389,11 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public void generateLocalOobData(@Transport int transport, @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) { if (transport != BluetoothDevice.TRANSPORT_BREDR && transport @@ -3169,11 +3403,11 @@ public final class BluetoothAdapter { requireNonNull(callback); if (!isEnabled()) { Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!"); - callback.onError(OOB_ERROR_ADAPTER_DISABLED); + callback.onError(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED); } else { try { mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback, - executor)); + executor), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -3203,12 +3437,14 @@ public final class BluetoothAdapter { * reason. If Bluetooth is already on and if this function is called to turn * it on, the api will return true and a callback will be called. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} - * * @param on True for on, false for off. * @param callback The callback to notify changes to the state. * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public boolean changeApplicationBluetoothState(boolean on, BluetoothStateChangeCallback callback) { return false; @@ -3240,8 +3476,8 @@ public final class BluetoothAdapter { } } - private Set<BluetoothDevice> toDeviceSet(BluetoothDevice[] devices) { - Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(Arrays.asList(devices)); + private Set<BluetoothDevice> toDeviceSet(List<BluetoothDevice> devices) { + Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(devices); return Collections.unmodifiableSet(deviceSet); } @@ -3306,11 +3542,19 @@ public final class BluetoothAdapter { && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11; } + /** {@hide} */ @UnsupportedAppUsage - /*package*/ IBluetoothManager getBluetoothManager() { + @RequiresNoPermission + public IBluetoothManager getBluetoothManager() { return mManagerService; } + /** {@hide} */ + @RequiresNoPermission + public AttributionSource getAttributionSource() { + return mAttributionSource; + } + private final ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks = new ArrayList<IBluetoothManagerCallback>(); @@ -3352,6 +3596,61 @@ public final class BluetoothAdapter { } /** + * Register a callback to receive events whenever the bluetooth stack goes down and back up, + * e.g. in the event the bluetooth is turned off/on via settings. + * + * If the bluetooth stack is currently up, there will not be an initial callback call. + * You can use the return value as an indication of this being the case. + * + * Callbacks will be delivered on a binder thread. + * + * @return whether bluetooth is already up currently + * + * @hide + */ + public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) { + return getBluetoothService(callback.mRemote) != null; + } + + /** + * Unregister a callback registered via {@link #registerServiceLifecycleCallback} + * + * @hide + */ + public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) { + removeServiceStateCallback(callback.mRemote); + } + + /** + * A callback for {@link #registerServiceLifecycleCallback} + * + * @hide + */ + public abstract static class ServiceLifecycleCallback { + + /** Called when the bluetooth stack is up */ + public abstract void onBluetoothServiceUp(); + + /** Called when the bluetooth stack is down */ + public abstract void onBluetoothServiceDown(); + + IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() { + @Override + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + ServiceLifecycleCallback.this.onBluetoothServiceUp(); + } + + @Override + public void onBluetoothServiceDown() { + ServiceLifecycleCallback.this.onBluetoothServiceDown(); + } + + @Override + public void onBrEdrDown() {} + }; + } + + /** * Starts a scan for Bluetooth LE devices. * * <p>Results of the scan are reported using the @@ -3363,7 +3662,10 @@ public final class BluetoothAdapter { * instead. */ @Deprecated - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startLeScan(LeScanCallback callback) { return startLeScan(null, callback); } @@ -3382,7 +3684,10 @@ public final class BluetoothAdapter { * instead. */ @Deprecated - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) { if (DBG) { Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids)); @@ -3416,6 +3721,7 @@ public final class BluetoothAdapter { return false; } + @SuppressLint("AndroidFrameworkBluetoothPermission") ScanCallback scanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { @@ -3479,7 +3785,9 @@ public final class BluetoothAdapter { * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead. */ @Deprecated - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopLeScan(LeScanCallback callback) { if (DBG) { Log.d(TAG, "stopLeScan()"); @@ -3520,7 +3828,9 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions, or unable to start this CoC */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull BluetoothServerSocket listenUsingL2capChannel() throws IOException { BluetoothServerSocket socket = @@ -3566,7 +3876,9 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions, or unable to start this CoC */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull BluetoothServerSocket listenUsingInsecureL2capChannel() throws IOException { BluetoothServerSocket socket = @@ -3611,7 +3923,11 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device, @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) { if (DBG) Log.d(TAG, "addOnMetadataChangedListener()"); @@ -3631,13 +3947,13 @@ public final class BluetoothAdapter { throw new NullPointerException("executor is null"); } - synchronized (sMetadataListeners) { + synchronized (mMetadataListeners) { List<Pair<OnMetadataChangedListener, Executor>> listenerList = - sMetadataListeners.get(device); + mMetadataListeners.get(device); if (listenerList == null) { // Create new listener/executor list for registeration listenerList = new ArrayList<>(); - sMetadataListeners.put(device, listenerList); + mMetadataListeners.put(device, listenerList); } else { // Check whether this device was already registed by the lisenter if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) { @@ -3651,7 +3967,8 @@ public final class BluetoothAdapter { boolean ret = false; try { - ret = service.registerMetadataListener(sBluetoothMetadataListener, device); + ret = service.registerMetadataListener(mBluetoothMetadataListener, device, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "registerMetadataListener fail", e); } finally { @@ -3660,7 +3977,7 @@ public final class BluetoothAdapter { listenerList.remove(listenerPair); if (listenerList.isEmpty()) { // Remove the device if its listener list is empty - sMetadataListeners.remove(device); + mMetadataListeners.remove(device); } } } @@ -3684,7 +4001,11 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device, @NonNull OnMetadataChangedListener listener) { if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()"); @@ -3695,24 +4016,24 @@ public final class BluetoothAdapter { throw new NullPointerException("listener is null"); } - synchronized (sMetadataListeners) { - if (!sMetadataListeners.containsKey(device)) { + synchronized (mMetadataListeners) { + if (!mMetadataListeners.containsKey(device)) { throw new IllegalArgumentException("device was not registered"); } // Remove issued listener from the registered device - sMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener))); + mMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener))); - if (sMetadataListeners.get(device).isEmpty()) { + if (mMetadataListeners.get(device).isEmpty()) { // Unregister to Bluetooth service if all listeners are removed from // the registered device - sMetadataListeners.remove(device); + mMetadataListeners.remove(device); final IBluetooth service = mService; if (service == null) { // Bluetooth is OFF, do nothing to Bluetooth service. return true; } try { - return service.unregisterMetadataListener(device); + return service.unregisterMetadataListener(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "unregisterMetadataListener fail", e); return false; @@ -3740,10 +4061,12 @@ public final class BluetoothAdapter { @Nullable byte[] value); } + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothConnectionCallback mConnectionCallback = new IBluetoothConnectionCallback.Stub() { @Override public void onDeviceConnected(BluetoothDevice device) { + Attributable.setAttributionSource(device, mAttributionSource); for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry: mBluetoothConnectionCallbackExecutorMap.entrySet()) { BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); @@ -3754,6 +4077,7 @@ public final class BluetoothAdapter { @Override public void onDeviceDisconnected(BluetoothDevice device, int hciReason) { + Attributable.setAttributionSource(device, mAttributionSource); for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry: mBluetoothConnectionCallbackExecutorMap.entrySet()) { BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); @@ -3773,6 +4097,11 @@ public final class BluetoothAdapter { * @throws IllegalArgumentException if the callback is already registered * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor, @NonNull BluetoothConnectionCallback callback) { if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()"); @@ -3786,7 +4115,8 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) { + if (!mService.registerBluetoothConnectionCallback(mConnectionCallback, + mAttributionSource)) { return false; } } @@ -3815,6 +4145,11 @@ public final class BluetoothAdapter { * @return true if the callback was unregistered successfully, false otherwise * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean unregisterBluetoothConnectionCallback( @NonNull BluetoothConnectionCallback callback) { if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()"); @@ -3836,7 +4171,8 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.unregisterBluetoothConnectionCallback(mConnectionCallback); + return mService.unregisterBluetoothConnectionCallback(mConnectionCallback, + mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -3872,141 +4208,45 @@ public final class BluetoothAdapter { */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "REASON_" }, value = { - REASON_UNKNOWN, - REASON_LOCAL_REQUEST, - REASON_REMOTE_REQUEST, - REASON_LOCAL_ERROR, - REASON_REMOTE_ERROR, - REASON_TIMEOUT, - REASON_SECURITY, - REASON_SYSTEM_POLICY, - REASON_RESOURCE_LIMIT_REACHED, - REASON_CONNECTION_EXISTS, - REASON_BAD_PARAMETERS}) + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS}) public @interface DisconnectReason {} /** - * Indicates that the ACL disconnected due to an unknown reason. - */ - public static final int REASON_UNKNOWN = 0; - - /** - * Indicates that the ACL disconnected due to an explicit request from the local device. - * <p> - * Example cause: This is a normal disconnect reason, e.g., user/app initiates - * disconnection. - */ - public static final int REASON_LOCAL_REQUEST = 1; - - /** - * Indicates that the ACL disconnected due to an explicit request from the remote device. - * <p> - * Example cause: This is a normal disconnect reason, e.g., user/app initiates - * disconnection. - * <p> - * Example solution: The app can also prompt the user to check their remote device. - */ - public static final int REASON_REMOTE_REQUEST = 2; - - /** - * Generic disconnect reason indicating the ACL disconnected due to an error on the local - * device. - * <p> - * Example solution: Prompt the user to check their local device (e.g., phone, car - * headunit). - */ - public static final int REASON_LOCAL_ERROR = 3; - - /** - * Generic disconnect reason indicating the ACL disconnected due to an error on the remote - * device. - * <p> - * Example solution: Prompt the user to check their remote device (e.g., headset, car - * headunit, watch). - */ - public static final int REASON_REMOTE_ERROR = 4; - - /** - * Indicates that the ACL disconnected due to a timeout. - * <p> - * Example cause: remote device might be out of range. - * <p> - * Example solution: Prompt user to verify their remote device is on or in - * connection/pairing mode. - */ - public static final int REASON_TIMEOUT = 5; - - /** - * Indicates that the ACL disconnected due to link key issues. - * <p> - * Example cause: Devices are either unpaired or remote device is refusing our pairing - * request. - * <p> - * Example solution: Prompt user to unpair and pair again. - */ - public static final int REASON_SECURITY = 6; - - /** - * Indicates that the ACL disconnected due to the local device's system policy. - * <p> - * Example cause: privacy policy, power management policy, permissions, etc. - * <p> - * Example solution: Prompt the user to check settings, or check with their system - * administrator (e.g. some corp-managed devices do not allow OPP connection). - */ - public static final int REASON_SYSTEM_POLICY = 7; - - /** - * Indicates that the ACL disconnected due to resource constraints, either on the local - * device or the remote device. - * <p> - * Example cause: controller is busy, memory limit reached, maximum number of connections - * reached. - * <p> - * Example solution: The app should wait and try again. If still failing, prompt the user - * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device. - */ - public static final int REASON_RESOURCE_LIMIT_REACHED = 8; - - /** - * Indicates that the ACL disconnected because another ACL connection already exists. - */ - public static final int REASON_CONNECTION_EXISTS = 9; - - /** - * Indicates that the ACL disconnected due to incorrect parameters passed in from the app. - * <p> - * Example solution: Change parameters and try again. If error persists, the app can report - * telemetry and/or log the error in a bugreport. - */ - public static final int REASON_BAD_PARAMETERS = 10; - - /** * Returns human-readable strings corresponding to {@link DisconnectReason}. */ public static String disconnectReasonText(@DisconnectReason int reason) { switch (reason) { - case REASON_UNKNOWN: + case BluetoothStatusCodes.ERROR_UNKNOWN: return "Reason unknown"; - case REASON_LOCAL_REQUEST: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST: return "Local request"; - case REASON_REMOTE_REQUEST: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST: return "Remote request"; - case REASON_LOCAL_ERROR: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL: return "Local error"; - case REASON_REMOTE_ERROR: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE: return "Remote error"; - case REASON_TIMEOUT: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT: return "Timeout"; - case REASON_SECURITY: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY: return "Security"; - case REASON_SYSTEM_POLICY: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY: return "System policy"; - case REASON_RESOURCE_LIMIT_REACHED: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED: return "Resource constrained"; - case REASON_CONNECTION_EXISTS: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS: return "Connection already exists"; - case REASON_BAD_PARAMETERS: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS: return "Bad parameters"; default: return "Unrecognized disconnect reason: " + reason; |
