diff options
Diffstat (limited to 'framework/java/android/bluetooth/BluetoothAdapter.java')
| -rw-r--r-- | framework/java/android/bluetooth/BluetoothAdapter.java | 184 |
1 files changed, 124 insertions, 60 deletions
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 5998ed585b..5116e05ee3 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -64,6 +64,8 @@ import android.os.SystemProperties; import android.util.Log; import android.util.Pair; +import com.android.internal.annotations.GuardedBy; + import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -78,6 +80,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.WeakHashMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -715,10 +718,21 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private final AttributionSource mAttributionSource; + // Yeah, keeping both mService and sService isn't pretty, but it's too late + // in the current release for a major refactoring, so we leave them both + // intact until this can be cleaned up in a future release + @UnsupportedAppUsage + @GuardedBy("mServiceLock") private IBluetooth mService; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); + @GuardedBy("sServiceLock") + private static boolean sServiceRegistered; + @GuardedBy("sServiceLock") + private static IBluetooth sService; + private static final Object sServiceLock = new Object(); + private final Object mLock = new Object(); private final Map<LeScanCallback, ScanCallback> mLeScanClients; private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>> @@ -792,19 +806,11 @@ public final class BluetoothAdapter { * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) { - if (managerService == null) { - throw new IllegalArgumentException("bluetooth manager service is null"); - } - try { - mServiceLock.writeLock().lock(); - mService = managerService.registerAdapter(mManagerCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.writeLock().unlock(); - } mManagerService = Objects.requireNonNull(managerService); mAttributionSource = Objects.requireNonNull(attributionSource); + synchronized (mServiceLock.writeLock()) { + mService = getBluetoothService(mManagerCallback); + } mLeScanClients = new HashMap<LeScanCallback, ScanCallback>(); mToken = new Binder(DESCRIPTOR); } @@ -3155,21 +3161,16 @@ public final class BluetoothAdapter { } } - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothManagerCallback mManagerCallback = + private static final IBluetoothManagerCallback sManagerCallback = new IBluetoothManagerCallback.Stub() { - @SuppressLint("AndroidFrameworkRequiresPermission") public void onBluetoothServiceUp(IBluetooth bluetoothService) { if (DBG) { Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); } - mServiceLock.writeLock().lock(); - mService = bluetoothService; - mServiceLock.writeLock().unlock(); - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { + synchronized (sServiceLock) { + sService = bluetoothService; + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { try { if (cb != null) { cb.onBluetoothServiceUp(bluetoothService); @@ -3181,6 +3182,56 @@ public final class BluetoothAdapter { } } } + } + + public void onBluetoothServiceDown() { + if (DBG) { + Log.d(TAG, "onBluetoothServiceDown"); + } + + synchronized (sServiceLock) { + sService = null; + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { + try { + if (cb != null) { + cb.onBluetoothServiceDown(); + } else { + Log.d(TAG, "onBluetoothServiceDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + } + } + + public void onBrEdrDown() { + if (VDBG) { + Log.i(TAG, "onBrEdrDown"); + } + + synchronized (sServiceLock) { + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { + try { + if (cb != null) { + cb.onBrEdrDown(); + } else { + Log.d(TAG, "onBrEdrDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + } + } + }; + + private final IBluetoothManagerCallback mManagerCallback = + new IBluetoothManagerCallback.Stub() { + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + synchronized (mServiceLock.writeLock()) { + mService = bluetoothService; + } synchronized (mMetadataListeners) { mMetadataListeners.forEach((device, pair) -> { try { @@ -3205,12 +3256,7 @@ public final class BluetoothAdapter { } public void onBluetoothServiceDown() { - if (DBG) { - Log.d(TAG, "onBluetoothServiceDown: " + mService); - } - - try { - mServiceLock.writeLock().lock(); + synchronized (mServiceLock.writeLock()) { mService = null; if (mLeScanClients != null) { mLeScanClients.clear(); @@ -3221,29 +3267,10 @@ public final class BluetoothAdapter { if (mBluetoothLeScanner != null) { mBluetoothLeScanner.cleanup(); } - } finally { - mServiceLock.writeLock().unlock(); - } - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { - try { - if (cb != null) { - cb.onBluetoothServiceDown(); - } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!"); - } - } catch (Exception e) { - Log.e(TAG, "", e); - } - } } } public void onBrEdrDown() { - if (VDBG) { - Log.i(TAG, "onBrEdrDown: " + mService); - } } }; @@ -3478,15 +3505,12 @@ public final class BluetoothAdapter { protected void finalize() throws Throwable { try { - mManagerService.unregisterAdapter(mManagerCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); + removeServiceStateCallback(mManagerCallback); } finally { super.finalize(); } } - /** * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" * <p>Alphabetic characters must be uppercase to be valid. @@ -3550,24 +3574,64 @@ public final class BluetoothAdapter { return mAttributionSource; } - private final ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks = - new ArrayList<IBluetoothManagerCallback>(); + @GuardedBy("sServiceLock") + private static final WeakHashMap<IBluetoothManagerCallback, Void> sProxyServiceStateCallbacks = + new WeakHashMap<>(); + + /*package*/ IBluetooth getBluetoothService() { + synchronized (sServiceLock) { + if (sProxyServiceStateCallbacks.isEmpty()) { + throw new IllegalStateException( + "Anonymous service access requires at least one lifecycle in process"); + } + return sService; + } + } @UnsupportedAppUsage /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { - synchronized (mProxyServiceStateCallbacks) { - if (cb == null) { - Log.w(TAG, "getBluetoothService() called with no BluetoothManagerCallback"); - } else if (!mProxyServiceStateCallbacks.contains(cb)) { - mProxyServiceStateCallbacks.add(cb); - } + Objects.requireNonNull(cb); + synchronized (sServiceLock) { + sProxyServiceStateCallbacks.put(cb, null); + registerOrUnregisterAdapterLocked(); + return sService; } - return mService; } /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) { - synchronized (mProxyServiceStateCallbacks) { - mProxyServiceStateCallbacks.remove(cb); + Objects.requireNonNull(cb); + synchronized (sServiceLock) { + sProxyServiceStateCallbacks.remove(cb); + registerOrUnregisterAdapterLocked(); + } + } + + /** + * Handle registering (or unregistering) a single process-wide + * {@link IBluetoothManagerCallback} based on the presence of local + * {@link #sProxyServiceStateCallbacks} clients. + */ + @GuardedBy("sServiceLock") + private void registerOrUnregisterAdapterLocked() { + final boolean isRegistered = sServiceRegistered; + final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty(); + + if (isRegistered != wantRegistered) { + if (wantRegistered) { + try { + sService = mManagerService.registerAdapter(sManagerCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + try { + mManagerService.unregisterAdapter(sManagerCallback); + sService = null; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + sServiceRegistered = wantRegistered; } } |
