summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp77
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java36
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterService.java13
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterSuspend.java120
-rw-r--r--flags/system_service.aconfig7
5 files changed, 253 insertions, 0 deletions
diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 72f1e7ce8a..0d69e44939 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -2143,6 +2143,73 @@ static jint getSocketL2capRemoteChannelIdNative(JNIEnv* /* env */, jobject /* ob
return (jint)cid;
}
+static jboolean setDefaultEventMaskExceptNative(JNIEnv* /* env */, jobject /* obj */, jlong mask,
+ jlong le_mask) {
+ log::verbose("");
+
+ if (!sBluetoothInterface) {
+ return JNI_FALSE;
+ }
+
+ int ret = sBluetoothInterface->set_default_event_mask_except(mask, le_mask);
+ return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean clearEventFilterNative(JNIEnv* /* env */, jobject /* obj */) {
+ log::verbose("");
+
+ if (!sBluetoothInterface) {
+ return JNI_FALSE;
+ }
+
+ int ret = sBluetoothInterface->clear_event_filter();
+ return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean clearFilterAcceptListNative(JNIEnv* /* env */, jobject /* obj */) {
+ log::verbose("");
+
+ if (!sBluetoothInterface) {
+ return JNI_FALSE;
+ }
+
+ int ret = sBluetoothInterface->clear_filter_accept_list();
+ return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean disconnectAllAclsNative(JNIEnv* /* env */, jobject /* obj */) {
+ log::verbose("");
+
+ if (!sBluetoothInterface) {
+ return JNI_FALSE;
+ }
+
+ int ret = sBluetoothInterface->disconnect_all_acls();
+ return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean allowWakeByHidNative(JNIEnv* /* env */, jobject /* obj */) {
+ log::verbose("");
+
+ if (!sBluetoothInterface) {
+ return JNI_FALSE;
+ }
+
+ int ret = sBluetoothInterface->allow_wake_by_hid();
+ return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean restoreFilterAcceptListNative(JNIEnv* /* env */, jobject /* obj */) {
+ log::verbose("");
+
+ if (!sBluetoothInterface) {
+ return JNI_FALSE;
+ }
+
+ int ret = sBluetoothInterface->restore_filter_accept_list();
+ return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {
const JNINativeMethod methods[] = {
{"initNative", "(ZZI[Ljava/lang/String;ZLjava/lang/String;)Z",
@@ -2207,6 +2274,16 @@ int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {
reinterpret_cast<void*>(getSocketL2capLocalChannelIdNative)},
{"getSocketL2capRemoteChannelIdNative", "(JJ)I",
reinterpret_cast<void*>(getSocketL2capRemoteChannelIdNative)},
+ {"setDefaultEventMaskExceptNative", "(JJ)Z",
+ reinterpret_cast<void*>(setDefaultEventMaskExceptNative)},
+ {"clearEventFilterNative", "()Z", reinterpret_cast<void*>(clearEventFilterNative)},
+ {"clearFilterAcceptListNative", "()Z",
+ reinterpret_cast<void*>(clearFilterAcceptListNative)},
+ {"disconnectAllAclsNative", "()Z", reinterpret_cast<void*>(disconnectAllAclsNative)},
+ {"allowWakeByHidNative", "()Z", reinterpret_cast<void*>(allowWakeByHidNative)},
+ {"restoreFilterAcceptListNative", "()Z",
+ reinterpret_cast<void*>(restoreFilterAcceptListNative)},
+
};
const int result = REGISTER_NATIVE_METHODS(
env, "com/android/bluetooth/btservice/AdapterNativeInterface", methods);
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
index 1ab1a7ea0c..28bff10bb7 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
@@ -259,6 +259,30 @@ public class AdapterNativeInterface {
connectionUuid.getUuid().getMostSignificantBits());
}
+ boolean setDefaultEventMaskExcept(long mask, long leMask) {
+ return setDefaultEventMaskExceptNative(mask, leMask);
+ }
+
+ boolean clearEventFilter() {
+ return clearEventFilterNative();
+ }
+
+ boolean clearFilterAcceptList() {
+ return clearFilterAcceptListNative();
+ }
+
+ boolean disconnectAllAcls() {
+ return disconnectAllAclsNative();
+ }
+
+ boolean allowWakeByHid() {
+ return allowWakeByHidNative();
+ }
+
+ boolean restoreFilterAcceptList() {
+ return restoreFilterAcceptListNative();
+ }
+
/**********************************************************************************************/
/*********************************** callbacks from native ************************************/
/**********************************************************************************************/
@@ -371,4 +395,16 @@ public class AdapterNativeInterface {
private native int getSocketL2capRemoteChannelIdNative(
long connectionUuidLsb, long connectionUuidMsb);
+
+ private native boolean setDefaultEventMaskExceptNative(long mask, long leMask);
+
+ private native boolean clearEventFilterNative();
+
+ private native boolean clearFilterAcceptListNative();
+
+ private native boolean disconnectAllAclsNative();
+
+ private native boolean allowWakeByHidNative();
+
+ private native boolean restoreFilterAcceptListNative();
}
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index 62a532dccd..5acdcd0197 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -88,6 +88,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManager;
import android.os.AsyncTask;
import android.os.BatteryStatsManager;
import android.os.Binder;
@@ -281,6 +282,7 @@ public class AdapterService extends Service {
private AdapterState mAdapterStateMachine;
private BondStateMachine mBondStateMachine;
private RemoteDevices mRemoteDevices;
+ private AdapterSuspend mAdapterSuspend;
/* TODO: Consider to remove the search API from this class, if changed to use call-back */
private SdpManager mSdpManager = null;
@@ -755,6 +757,12 @@ public class AdapterService extends Service {
mBluetoothSocketManagerBinder = new BluetoothSocketManagerBinder(this);
+ if (Flags.adapterSuspendMgmt()) {
+ mAdapterSuspend =
+ new AdapterSuspend(
+ mNativeInterface, mLooper, getSystemService(DisplayManager.class));
+ }
+
if (!Flags.fastBindToApp()) {
setAdapterService(this);
}
@@ -1508,6 +1516,11 @@ public class AdapterService extends Service {
mBluetoothSocketManagerBinder = null;
}
+ if (mAdapterSuspend != null) {
+ mAdapterSuspend.cleanup();
+ mAdapterSuspend = null;
+ }
+
mPreferredAudioProfilesCallbacks.kill();
mBluetoothQualityReportReadyCallbacks.kill();
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterSuspend.java b/android/app/src/com/android/bluetooth/btservice/AdapterSuspend.java
new file mode 100644
index 0000000000..ab5187b14b
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterSuspend.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.btservice;
+
+import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
+import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
+
+import static java.util.Objects.requireNonNull;
+
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.Display;
+
+import java.util.Arrays;
+
+public class AdapterSuspend {
+ private static final String TAG = "BtAdapterSuspend";
+
+ // Event mask bits corresponding to specific HCI events
+ // as defined in Bluetooth core v5.4, Vol 4, Part E, 7.3.1.
+ private static final long MASK_DISCONNECT_CMPLT = 1 << 4;
+ private static final long MASK_MODE_CHANGE = 1 << 19;
+
+ private boolean mSuspended = false;
+
+ private final AdapterNativeInterface mAdapterNativeInterface;
+ private final Looper mLooper;
+ private final DisplayManager mDisplayManager;
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayRemoved(int displayId) {}
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (isScreenOn()) {
+ handleResume();
+ } else {
+ handleSuspend();
+ }
+ }
+ };
+
+ public AdapterSuspend(
+ AdapterNativeInterface adapterNativeInterface,
+ Looper looper,
+ DisplayManager displayManager) {
+ mAdapterNativeInterface = requireNonNull(adapterNativeInterface);
+ mLooper = requireNonNull(looper);
+ mDisplayManager = requireNonNull(displayManager);
+
+ mDisplayManager.registerDisplayListener(mDisplayListener, new Handler(mLooper));
+ }
+
+ void cleanup() {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ }
+
+ private boolean isScreenOn() {
+ return Arrays.stream(mDisplayManager.getDisplays())
+ .anyMatch(display -> display.getState() == Display.STATE_ON);
+ }
+
+ private void handleSuspend() {
+ if (mSuspended) {
+ return;
+ }
+ mSuspended = true;
+
+ long mask = MASK_DISCONNECT_CMPLT | MASK_MODE_CHANGE;
+ long leMask = 0;
+
+ // Avoid unexpected interrupt during suspend.
+ mAdapterNativeInterface.setDefaultEventMaskExcept(mask, leMask);
+
+ // Disable inquiry scan and page scan.
+ mAdapterNativeInterface.setScanMode(AdapterService.convertScanModeToHal(SCAN_MODE_NONE));
+
+ mAdapterNativeInterface.clearEventFilter();
+ mAdapterNativeInterface.clearFilterAcceptList();
+ mAdapterNativeInterface.disconnectAllAcls();
+ mAdapterNativeInterface.allowWakeByHid();
+ Log.i(TAG, "ready to suspend");
+ }
+
+ private void handleResume() {
+ if (!mSuspended) {
+ return;
+ }
+ mSuspended = false;
+
+ long mask = 0;
+ long leMask = 0;
+ mAdapterNativeInterface.setDefaultEventMaskExcept(mask, leMask);
+ mAdapterNativeInterface.clearEventFilter();
+ mAdapterNativeInterface.restoreFilterAcceptList();
+ mAdapterNativeInterface.setScanMode(
+ AdapterService.convertScanModeToHal(SCAN_MODE_CONNECTABLE));
+ Log.i(TAG, "resumed");
+ }
+}
diff --git a/flags/system_service.aconfig b/flags/system_service.aconfig
index 1c40504d97..df29c6f3af 100644
--- a/flags/system_service.aconfig
+++ b/flags/system_service.aconfig
@@ -84,3 +84,10 @@ flag {
description: "Replace binder call to the system server with a Messenger to enforce thread safety"
bug: "321804999"
}
+
+flag {
+ name: "adapter_suspend_mgmt"
+ namespace: "bluetooth"
+ description: "Configure the BT adapter in a suspend state to avoid unexpected wake-up"
+ bug: "366432079"
+}