summaryrefslogtreecommitdiff
path: root/framework/java/android/bluetooth/BluetoothMap.java
diff options
context:
space:
mode:
authorWilliam Escande <wescande@google.com>2022-04-22 15:49:39 -0700
committerWilliam Escande <wescande@google.com>2022-04-29 22:39:43 +0000
commit25fc56ed6a3506d3c83cd01803a3b34e5ffff4ca (patch)
tree3664a30b0cee81565ed8ab3044e7650597f499b4 /framework/java/android/bluetooth/BluetoothMap.java
parent89f4da04fe1059d734c11247e0167f4685194c82 (diff)
Add caching for {Map,Sap}.getConnectionState
BluetoothMap and BluetoothSap are heavily using their getConnectionState method. By caching the value I intend to reduce drastically the number of binder calls generated. While performing a discovery, the caching is working on 80% of binder calls. This value should be bigger when we kepp asking the value for the same device. (currently everytime you ask the connection state of a new device, you refresh the cache, there is no caching for multiples devices). I don't have any Map/Sap setup to test it, asking to test team if they hit some issues Fixing cache on BluetoothDevice by passing the device as parameter Doing a refactoring of all cached method in bluetooth to keep code consistency: AKA we try to do the most of check outside of the cache query Bug: 217366135 Test: Put some log to see the value being cached sometimes - need more testing Ignore-AOSP-First: No caching api on AOSP Change-Id: Iababf9f9068a181e277b400e786a4a67d4447dc8
Diffstat (limited to 'framework/java/android/bluetooth/BluetoothMap.java')
-rw-r--r--framework/java/android/bluetooth/BluetoothMap.java75
1 files changed, 66 insertions, 9 deletions
diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java
index 56e4972624..a8b48348da 100644
--- a/framework/java/android/bluetooth/BluetoothMap.java
+++ b/framework/java/android/bluetooth/BluetoothMap.java
@@ -32,9 +32,11 @@ import android.content.AttributionSource;
import android.content.Context;
import android.os.Build;
import android.os.IBinder;
+import android.os.IpcDataCache;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import android.util.Pair;
import com.android.modules.utils.SynchronousResultReceiver;
@@ -354,10 +356,65 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
}
/**
+ * There are several instances of IpcDataCache used in this class.
+ * BluetoothCache wraps up the common code. All caches are created with a maximum of
+ * eight entries, and the key is in the bluetooth module. The name is set to the api.
+ */
+ private static class BluetoothCache<Q, R> extends IpcDataCache<Q, R> {
+ BluetoothCache(String api, IpcDataCache.QueryHandler query) {
+ super(8, IpcDataCache.MODULE_BLUETOOTH, api, api, query);
+ }};
+
+ /** @hide */
+ public void disableBluetoothGetConnectionStateCache() {
+ mBluetoothConnectionCache.disableForCurrentProcess();
+ }
+
+ /** @hide */
+ public static void invalidateBluetoothGetConnectionStateCache() {
+ invalidateCache(GET_CONNECTION_STATE_API);
+ }
+
+ /**
+ * Invalidate a bluetooth cache. This method is just a short-hand wrapper that
+ * enforces the bluetooth module.
+ */
+ private static void invalidateCache(@NonNull String api) {
+ IpcDataCache.invalidateCache(IpcDataCache.MODULE_BLUETOOTH, api);
+ }
+
+ private final IpcDataCache.QueryHandler<Pair<IBluetoothMap, BluetoothDevice>, Integer>
+ mBluetoothConnectionQuery = new IpcDataCache.QueryHandler<>() {
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @Override
+ public Integer apply(Pair<IBluetoothMap, BluetoothDevice> pairQuery) {
+ if (DBG) {
+ log("getConnectionState(" + pairQuery.second.getAnonymizedAddress()
+ + ") uncached");
+ }
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ try {
+ pairQuery.first
+ .getConnectionState(pairQuery.second, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout())
+ .getValue(BluetoothProfile.STATE_DISCONNECTED);
+ } catch (RemoteException | TimeoutException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ private static final String GET_CONNECTION_STATE_API = "BluetoothMap_getConnectionState";
+
+ private final BluetoothCache<Pair<IBluetoothMap, BluetoothDevice>, Integer>
+ mBluetoothConnectionCache = new BluetoothCache<>(GET_CONNECTION_STATE_API,
+ mBluetoothConnectionQuery);
+
+ /**
* Get connection state of device
*
* @return device connection state
- *
* @hide
*/
@RequiresBluetoothConnectPermission
@@ -365,21 +422,21 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothMap service = getService();
- final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ Log.w(TAG, "BT not enabled. Cannot get connection state");
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (isEnabled() && isValidDevice(device)) {
try {
- final SynchronousResultReceiver<Integer> recv =
- new SynchronousResultReceiver();
- service.getConnectionState(device, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
- } catch (RemoteException | TimeoutException e) {
+ return mBluetoothConnectionCache.query(new Pair<>(service, device));
+ } catch (RuntimeException e) {
+ if (!(e.getCause() instanceof TimeoutException)
+ && !(e.getCause() instanceof RemoteException)) {
+ throw e;
+ }
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return defaultValue;
+ return BluetoothProfile.STATE_DISCONNECTED;
}
/**