Loading android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp +77 −0 Original line number Original line Diff line number Diff line Loading @@ -2143,6 +2143,73 @@ static jint getSocketL2capRemoteChannelIdNative(JNIEnv* /* env */, jobject /* ob return (jint)cid; 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) { int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) { const JNINativeMethod methods[] = { const JNINativeMethod methods[] = { {"initNative", "(ZZI[Ljava/lang/String;ZLjava/lang/String;)Z", {"initNative", "(ZZI[Ljava/lang/String;ZLjava/lang/String;)Z", Loading Loading @@ -2207,6 +2274,16 @@ int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) { reinterpret_cast<void*>(getSocketL2capLocalChannelIdNative)}, reinterpret_cast<void*>(getSocketL2capLocalChannelIdNative)}, {"getSocketL2capRemoteChannelIdNative", "(JJ)I", {"getSocketL2capRemoteChannelIdNative", "(JJ)I", reinterpret_cast<void*>(getSocketL2capRemoteChannelIdNative)}, 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( const int result = REGISTER_NATIVE_METHODS( env, "com/android/bluetooth/btservice/AdapterNativeInterface", methods); env, "com/android/bluetooth/btservice/AdapterNativeInterface", methods); Loading android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java +36 −0 Original line number Original line Diff line number Diff line Loading @@ -259,6 +259,30 @@ public class AdapterNativeInterface { connectionUuid.getUuid().getMostSignificantBits()); 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 ************************************/ /*********************************** callbacks from native ************************************/ /**********************************************************************************************/ /**********************************************************************************************/ Loading Loading @@ -371,4 +395,16 @@ public class AdapterNativeInterface { private native int getSocketL2capRemoteChannelIdNative( private native int getSocketL2capRemoteChannelIdNative( long connectionUuidLsb, long connectionUuidMsb); 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(); } } android/app/src/com/android/bluetooth/btservice/AdapterService.java +13 −0 Original line number Original line Diff line number Diff line Loading @@ -88,6 +88,7 @@ import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.PackageManager; import android.hardware.display.DisplayManager; import android.os.AsyncTask; import android.os.AsyncTask; import android.os.BatteryStatsManager; import android.os.BatteryStatsManager; import android.os.Binder; import android.os.Binder; Loading Loading @@ -281,6 +282,7 @@ public class AdapterService extends Service { private AdapterState mAdapterStateMachine; private AdapterState mAdapterStateMachine; private BondStateMachine mBondStateMachine; private BondStateMachine mBondStateMachine; private RemoteDevices mRemoteDevices; private RemoteDevices mRemoteDevices; private AdapterSuspend mAdapterSuspend; /* TODO: Consider to remove the search API from this class, if changed to use call-back */ /* TODO: Consider to remove the search API from this class, if changed to use call-back */ private SdpManager mSdpManager = null; private SdpManager mSdpManager = null; Loading Loading @@ -755,6 +757,12 @@ public class AdapterService extends Service { mBluetoothSocketManagerBinder = new BluetoothSocketManagerBinder(this); mBluetoothSocketManagerBinder = new BluetoothSocketManagerBinder(this); if (Flags.adapterSuspendMgmt()) { mAdapterSuspend = new AdapterSuspend( mNativeInterface, mLooper, getSystemService(DisplayManager.class)); } if (!Flags.fastBindToApp()) { if (!Flags.fastBindToApp()) { setAdapterService(this); setAdapterService(this); } } Loading Loading @@ -1508,6 +1516,11 @@ public class AdapterService extends Service { mBluetoothSocketManagerBinder = null; mBluetoothSocketManagerBinder = null; } } if (mAdapterSuspend != null) { mAdapterSuspend.cleanup(); mAdapterSuspend = null; } mPreferredAudioProfilesCallbacks.kill(); mPreferredAudioProfilesCallbacks.kill(); mBluetoothQualityReportReadyCallbacks.kill(); mBluetoothQualityReportReadyCallbacks.kill(); Loading android/app/src/com/android/bluetooth/btservice/AdapterSuspend.java 0 → 100644 +120 −0 Original line number Original line Diff line number Diff line /* * 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"); } } flags/system_service.aconfig +7 −0 Original line number Original line Diff line number Diff line Loading @@ -84,3 +84,10 @@ flag { description: "Replace binder call to the system server with a Messenger to enforce thread safety" description: "Replace binder call to the system server with a Messenger to enforce thread safety" bug: "321804999" 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" } Loading
android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp +77 −0 Original line number Original line Diff line number Diff line Loading @@ -2143,6 +2143,73 @@ static jint getSocketL2capRemoteChannelIdNative(JNIEnv* /* env */, jobject /* ob return (jint)cid; 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) { int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) { const JNINativeMethod methods[] = { const JNINativeMethod methods[] = { {"initNative", "(ZZI[Ljava/lang/String;ZLjava/lang/String;)Z", {"initNative", "(ZZI[Ljava/lang/String;ZLjava/lang/String;)Z", Loading Loading @@ -2207,6 +2274,16 @@ int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) { reinterpret_cast<void*>(getSocketL2capLocalChannelIdNative)}, reinterpret_cast<void*>(getSocketL2capLocalChannelIdNative)}, {"getSocketL2capRemoteChannelIdNative", "(JJ)I", {"getSocketL2capRemoteChannelIdNative", "(JJ)I", reinterpret_cast<void*>(getSocketL2capRemoteChannelIdNative)}, 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( const int result = REGISTER_NATIVE_METHODS( env, "com/android/bluetooth/btservice/AdapterNativeInterface", methods); env, "com/android/bluetooth/btservice/AdapterNativeInterface", methods); Loading
android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java +36 −0 Original line number Original line Diff line number Diff line Loading @@ -259,6 +259,30 @@ public class AdapterNativeInterface { connectionUuid.getUuid().getMostSignificantBits()); 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 ************************************/ /*********************************** callbacks from native ************************************/ /**********************************************************************************************/ /**********************************************************************************************/ Loading Loading @@ -371,4 +395,16 @@ public class AdapterNativeInterface { private native int getSocketL2capRemoteChannelIdNative( private native int getSocketL2capRemoteChannelIdNative( long connectionUuidLsb, long connectionUuidMsb); 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(); } }
android/app/src/com/android/bluetooth/btservice/AdapterService.java +13 −0 Original line number Original line Diff line number Diff line Loading @@ -88,6 +88,7 @@ import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.PackageManager; import android.hardware.display.DisplayManager; import android.os.AsyncTask; import android.os.AsyncTask; import android.os.BatteryStatsManager; import android.os.BatteryStatsManager; import android.os.Binder; import android.os.Binder; Loading Loading @@ -281,6 +282,7 @@ public class AdapterService extends Service { private AdapterState mAdapterStateMachine; private AdapterState mAdapterStateMachine; private BondStateMachine mBondStateMachine; private BondStateMachine mBondStateMachine; private RemoteDevices mRemoteDevices; private RemoteDevices mRemoteDevices; private AdapterSuspend mAdapterSuspend; /* TODO: Consider to remove the search API from this class, if changed to use call-back */ /* TODO: Consider to remove the search API from this class, if changed to use call-back */ private SdpManager mSdpManager = null; private SdpManager mSdpManager = null; Loading Loading @@ -755,6 +757,12 @@ public class AdapterService extends Service { mBluetoothSocketManagerBinder = new BluetoothSocketManagerBinder(this); mBluetoothSocketManagerBinder = new BluetoothSocketManagerBinder(this); if (Flags.adapterSuspendMgmt()) { mAdapterSuspend = new AdapterSuspend( mNativeInterface, mLooper, getSystemService(DisplayManager.class)); } if (!Flags.fastBindToApp()) { if (!Flags.fastBindToApp()) { setAdapterService(this); setAdapterService(this); } } Loading Loading @@ -1508,6 +1516,11 @@ public class AdapterService extends Service { mBluetoothSocketManagerBinder = null; mBluetoothSocketManagerBinder = null; } } if (mAdapterSuspend != null) { mAdapterSuspend.cleanup(); mAdapterSuspend = null; } mPreferredAudioProfilesCallbacks.kill(); mPreferredAudioProfilesCallbacks.kill(); mBluetoothQualityReportReadyCallbacks.kill(); mBluetoothQualityReportReadyCallbacks.kill(); Loading
android/app/src/com/android/bluetooth/btservice/AdapterSuspend.java 0 → 100644 +120 −0 Original line number Original line Diff line number Diff line /* * 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"); } }
flags/system_service.aconfig +7 −0 Original line number Original line Diff line number Diff line Loading @@ -84,3 +84,10 @@ flag { description: "Replace binder call to the system server with a Messenger to enforce thread safety" description: "Replace binder call to the system server with a Messenger to enforce thread safety" bug: "321804999" 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" }