Loading service/java/com/android/server/bluetooth/BluetoothManagerService.java +132 −0 Original line number Diff line number Diff line Loading @@ -244,6 +244,8 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { private BluetoothNotificationManager mBluetoothNotificationManager; private BluetoothSatelliteModeListener mBluetoothSatelliteModeListener; // used inside handler thread private boolean mQuietEnable = false; private boolean mEnable; Loading Loading @@ -490,6 +492,79 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { } } final Runnable mOnSatelliteModeChangedRunnable = () -> { onSatelliteModeChanged(); }; @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) void onSatelliteModeChanged() { int delaySatelliteMs = 0; int state = getState(); Log.d(TAG, "onSatelliteModeChanged state : " + BluetoothAdapter.nameForState(state) + ", isSatelliteModeSensitive() : " + isSatelliteModeSensitive() + ", isSatelliteModeOn() : " + isSatelliteModeOn()); if (mHandler.hasCallbacks(mOnSatelliteModeChangedRunnable)) { mHandler.removeCallbacks(mOnSatelliteModeChangedRunnable); } if (state == BluetoothAdapter.STATE_BLE_ON && isBluetoothPersistedStateOn()) { delaySatelliteMs = SERVICE_RESTART_TIME_MS; } if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF && state != BluetoothAdapter.STATE_BLE_ON) { // If Bluetooth is turning state, should handle event after delay delaySatelliteMs = ADD_PROXY_DELAY_MS; } else if (mHandler.hasMessages(MESSAGE_ENABLE) || mHandler.hasMessages(MESSAGE_DISABLE) || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED) || mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mHandler.hasMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE) || mHandler.hasMessages(MESSAGE_TIMEOUT_BIND) || mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE)) { // If Bluetooth restarting, should handle event after delay delaySatelliteMs = SERVICE_RESTART_TIME_MS; } if (delaySatelliteMs > 0) { Log.d(TAG, "onSatelliteModeChanged delay MS : " + delaySatelliteMs); mHandler.postDelayed(mOnSatelliteModeChangedRunnable, delaySatelliteMs); } else { handleSatelliteModeChanged(); } } private void handleSatelliteModeChanged() { if (shouldBluetoothBeOn() && getState() != BluetoothAdapter.STATE_ON) { sendEnableMsg(mQuietEnableExternal, BluetoothProtoEnums.ENABLE_DISABLE_REASON_SATELLITE_MODE, mContext.getPackageName()); } else if (!shouldBluetoothBeOn() && getState() != BluetoothAdapter.STATE_OFF) { sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_SATELLITE_MODE, mContext.getPackageName()); } } private boolean shouldBluetoothBeOn() { if (!isBluetoothPersistedStateOn()) { Log.d(TAG, "shouldBluetoothBeOn: User want BT off."); return false; } if (isSatelliteModeOn()) { Log.d(TAG, "shouldBluetoothBeOn: BT should be off as satellite mode is on."); return false; } if (isAirplaneModeOn() && isBluetoothPersistedStateOnAirplane()) { Log.d(TAG, "shouldBluetoothBeOn: BT should be off as airplaneMode is on."); return false; } Log.d(TAG, "shouldBluetoothBeOn: BT should be on."); return true; } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading Loading @@ -684,6 +759,9 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { Log.w(TAG, "Unable to resolve SystemUI's UID."); } mSystemUiUid = systemUiUid; mBluetoothSatelliteModeListener = new BluetoothSatelliteModeListener( this, mBluetoothHandlerThread.getLooper(), context); } /** Loading @@ -694,6 +772,33 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { Settings.Global.AIRPLANE_MODE_ON, 0) == 1; } /** * @hide constant copied from {@link Settings.Global} * TODO(b/274636414): Migrate to official API in Android V. */ @VisibleForTesting static final String SETTINGS_SATELLITE_MODE_RADIOS = "satellite_mode_radios"; /** * @hide constant copied from {@link Settings.Global} * TODO(b/274636414): Migrate to official API in Android V. */ @VisibleForTesting static final String SETTINGS_SATELLITE_MODE_ENABLED = "satellite_mode_enabled"; private boolean isSatelliteModeSensitive() { final String satelliteRadios = Settings.Global.getString(mContext.getContentResolver(), SETTINGS_SATELLITE_MODE_RADIOS); return satelliteRadios != null && satelliteRadios.contains(Settings.Global.RADIO_BLUETOOTH); } /** Returns true if satellite mode is turned on. */ private boolean isSatelliteModeOn() { if (!isSatelliteModeSensitive()) return false; return Settings.Global.getInt(mContext.getContentResolver(), SETTINGS_SATELLITE_MODE_ENABLED, 0) == 1; } /** * Returns true if airplane mode enhancement feature is enabled */ Loading Loading @@ -1238,6 +1343,12 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { + " mBinding = " + mBinding + " mState = " + BluetoothAdapter.nameForState(mState)); } if (isSatelliteModeOn()) { Log.d(TAG, "enableBle(): not enabling - satellite mode is on."); return false; } updateBleAppCount(token, true, packageName); if (mState == BluetoothAdapter.STATE_ON Loading Loading @@ -1273,6 +1384,11 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { + BluetoothAdapter.nameForState(mState)); } if (isSatelliteModeOn()) { Log.d(TAG, "disableBle(): not disabling - satellite mode is on."); return false; } if (mState == BluetoothAdapter.STATE_OFF) { Log.d(TAG, "disableBLE(): Already disabled"); return false; Loading Loading @@ -1399,6 +1515,11 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { throw new SecurityException("no permission to enable Bluetooth quietly"); } if (isSatelliteModeOn()) { Log.d(TAG, "enableNoAutoConnect(): not enabling - satellite mode is on."); return false; } synchronized (mReceiver) { mQuietEnableExternal = true; mEnableExternal = true; Loading Loading @@ -1429,6 +1550,11 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { return false; } if (isSatelliteModeOn()) { Log.d(TAG, "enable(): not enabling - satellite mode is on."); return false; } if (DBG) { Log.d(TAG, "enable(" + packageName + "): mBluetooth=" + mBluetooth + " mBinding=" + mBinding + " mState=" + BluetoothAdapter.nameForState(mState)); Loading Loading @@ -1494,6 +1620,11 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { return false; } if (isSatelliteModeOn()) { Log.d(TAG, "disable: not disabling - satellite mode is on."); return false; } if (DBG) { Log.d(TAG, "disable(): mBluetooth=" + mBluetooth + ", persist=" + persist + ", mBinding=" + mBinding); Loading Loading @@ -3525,3 +3656,4 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { return BluetoothAdapter.BT_SNOOP_LOG_MODE_DISABLED; } } service/java/com/android/server/bluetooth/BluetoothSatelliteModeListener.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright 2023 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.server.bluetooth; import android.content.Context; import android.database.ContentObserver; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.provider.Settings; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; /** * The SatelliteModeListener handles system satellite mode change callback and inform * BluetoothManagerService on this change. */ public class BluetoothSatelliteModeListener { private static final String TAG = BluetoothSatelliteModeListener.class.getSimpleName(); private final BluetoothManagerService mBluetoothManagerService; private final BluetoothSatelliteModeHandler mHandler; private static final int MSG_SATELLITE_MODE_CHANGED = 0; BluetoothSatelliteModeListener(BluetoothManagerService service, Looper looper, Context context) { Log.d(TAG, " BluetoothSatelliteModeListener"); mBluetoothManagerService = service; mHandler = new BluetoothSatelliteModeHandler(looper); context.getContentResolver().registerContentObserver( Settings.Global.getUriFor(BluetoothManagerService.SETTINGS_SATELLITE_MODE_RADIOS), false, mSatelliteModeObserver); context.getContentResolver().registerContentObserver( Settings.Global.getUriFor(BluetoothManagerService.SETTINGS_SATELLITE_MODE_ENABLED), false, mSatelliteModeObserver); } private final ContentObserver mSatelliteModeObserver = new ContentObserver(null) { @Override public void onChange(boolean unused) { // Post from system main thread to android_io thread. mHandler.sendEmptyMessage(MSG_SATELLITE_MODE_CHANGED); } }; private class BluetoothSatelliteModeHandler extends Handler { BluetoothSatelliteModeHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { if (msg.what != MSG_SATELLITE_MODE_CHANGED) { Log.e(TAG, "Invalid message: " + msg.what); return; } handleSatelliteModeChange(); } } @VisibleForTesting public void handleSatelliteModeChange() { mBluetoothManagerService.onSatelliteModeChanged(); } } service/tests/src/com/android/server/bluetooth/BluetoothSatelliteModeListenerTest.java 0 → 100644 +60 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.server.bluetooth; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.content.Context; import android.os.Looper; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class BluetoothSatelliteModeListenerTest { private BluetoothSatelliteModeListener mBluetoothSatelliteModeListener; @Mock private Context mContext; @Mock private ContentResolver mContentResolver; @Mock private BluetoothManagerService mBluetoothManagerService; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mContext.getContentResolver()).thenReturn(mContentResolver); mBluetoothSatelliteModeListener = new BluetoothSatelliteModeListener( mBluetoothManagerService, Looper.getMainLooper(), mContext); } @Test public void testHandleSatelliteModeChange_InvokeSatelliteModeChanged() { mBluetoothSatelliteModeListener.handleSatelliteModeChange(); verify(mBluetoothManagerService).onSatelliteModeChanged(); } } Loading
service/java/com/android/server/bluetooth/BluetoothManagerService.java +132 −0 Original line number Diff line number Diff line Loading @@ -244,6 +244,8 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { private BluetoothNotificationManager mBluetoothNotificationManager; private BluetoothSatelliteModeListener mBluetoothSatelliteModeListener; // used inside handler thread private boolean mQuietEnable = false; private boolean mEnable; Loading Loading @@ -490,6 +492,79 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { } } final Runnable mOnSatelliteModeChangedRunnable = () -> { onSatelliteModeChanged(); }; @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) void onSatelliteModeChanged() { int delaySatelliteMs = 0; int state = getState(); Log.d(TAG, "onSatelliteModeChanged state : " + BluetoothAdapter.nameForState(state) + ", isSatelliteModeSensitive() : " + isSatelliteModeSensitive() + ", isSatelliteModeOn() : " + isSatelliteModeOn()); if (mHandler.hasCallbacks(mOnSatelliteModeChangedRunnable)) { mHandler.removeCallbacks(mOnSatelliteModeChangedRunnable); } if (state == BluetoothAdapter.STATE_BLE_ON && isBluetoothPersistedStateOn()) { delaySatelliteMs = SERVICE_RESTART_TIME_MS; } if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF && state != BluetoothAdapter.STATE_BLE_ON) { // If Bluetooth is turning state, should handle event after delay delaySatelliteMs = ADD_PROXY_DELAY_MS; } else if (mHandler.hasMessages(MESSAGE_ENABLE) || mHandler.hasMessages(MESSAGE_DISABLE) || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED) || mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mHandler.hasMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE) || mHandler.hasMessages(MESSAGE_TIMEOUT_BIND) || mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE)) { // If Bluetooth restarting, should handle event after delay delaySatelliteMs = SERVICE_RESTART_TIME_MS; } if (delaySatelliteMs > 0) { Log.d(TAG, "onSatelliteModeChanged delay MS : " + delaySatelliteMs); mHandler.postDelayed(mOnSatelliteModeChangedRunnable, delaySatelliteMs); } else { handleSatelliteModeChanged(); } } private void handleSatelliteModeChanged() { if (shouldBluetoothBeOn() && getState() != BluetoothAdapter.STATE_ON) { sendEnableMsg(mQuietEnableExternal, BluetoothProtoEnums.ENABLE_DISABLE_REASON_SATELLITE_MODE, mContext.getPackageName()); } else if (!shouldBluetoothBeOn() && getState() != BluetoothAdapter.STATE_OFF) { sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_SATELLITE_MODE, mContext.getPackageName()); } } private boolean shouldBluetoothBeOn() { if (!isBluetoothPersistedStateOn()) { Log.d(TAG, "shouldBluetoothBeOn: User want BT off."); return false; } if (isSatelliteModeOn()) { Log.d(TAG, "shouldBluetoothBeOn: BT should be off as satellite mode is on."); return false; } if (isAirplaneModeOn() && isBluetoothPersistedStateOnAirplane()) { Log.d(TAG, "shouldBluetoothBeOn: BT should be off as airplaneMode is on."); return false; } Log.d(TAG, "shouldBluetoothBeOn: BT should be on."); return true; } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading Loading @@ -684,6 +759,9 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { Log.w(TAG, "Unable to resolve SystemUI's UID."); } mSystemUiUid = systemUiUid; mBluetoothSatelliteModeListener = new BluetoothSatelliteModeListener( this, mBluetoothHandlerThread.getLooper(), context); } /** Loading @@ -694,6 +772,33 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { Settings.Global.AIRPLANE_MODE_ON, 0) == 1; } /** * @hide constant copied from {@link Settings.Global} * TODO(b/274636414): Migrate to official API in Android V. */ @VisibleForTesting static final String SETTINGS_SATELLITE_MODE_RADIOS = "satellite_mode_radios"; /** * @hide constant copied from {@link Settings.Global} * TODO(b/274636414): Migrate to official API in Android V. */ @VisibleForTesting static final String SETTINGS_SATELLITE_MODE_ENABLED = "satellite_mode_enabled"; private boolean isSatelliteModeSensitive() { final String satelliteRadios = Settings.Global.getString(mContext.getContentResolver(), SETTINGS_SATELLITE_MODE_RADIOS); return satelliteRadios != null && satelliteRadios.contains(Settings.Global.RADIO_BLUETOOTH); } /** Returns true if satellite mode is turned on. */ private boolean isSatelliteModeOn() { if (!isSatelliteModeSensitive()) return false; return Settings.Global.getInt(mContext.getContentResolver(), SETTINGS_SATELLITE_MODE_ENABLED, 0) == 1; } /** * Returns true if airplane mode enhancement feature is enabled */ Loading Loading @@ -1238,6 +1343,12 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { + " mBinding = " + mBinding + " mState = " + BluetoothAdapter.nameForState(mState)); } if (isSatelliteModeOn()) { Log.d(TAG, "enableBle(): not enabling - satellite mode is on."); return false; } updateBleAppCount(token, true, packageName); if (mState == BluetoothAdapter.STATE_ON Loading Loading @@ -1273,6 +1384,11 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { + BluetoothAdapter.nameForState(mState)); } if (isSatelliteModeOn()) { Log.d(TAG, "disableBle(): not disabling - satellite mode is on."); return false; } if (mState == BluetoothAdapter.STATE_OFF) { Log.d(TAG, "disableBLE(): Already disabled"); return false; Loading Loading @@ -1399,6 +1515,11 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { throw new SecurityException("no permission to enable Bluetooth quietly"); } if (isSatelliteModeOn()) { Log.d(TAG, "enableNoAutoConnect(): not enabling - satellite mode is on."); return false; } synchronized (mReceiver) { mQuietEnableExternal = true; mEnableExternal = true; Loading Loading @@ -1429,6 +1550,11 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { return false; } if (isSatelliteModeOn()) { Log.d(TAG, "enable(): not enabling - satellite mode is on."); return false; } if (DBG) { Log.d(TAG, "enable(" + packageName + "): mBluetooth=" + mBluetooth + " mBinding=" + mBinding + " mState=" + BluetoothAdapter.nameForState(mState)); Loading Loading @@ -1494,6 +1620,11 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { return false; } if (isSatelliteModeOn()) { Log.d(TAG, "disable: not disabling - satellite mode is on."); return false; } if (DBG) { Log.d(TAG, "disable(): mBluetooth=" + mBluetooth + ", persist=" + persist + ", mBinding=" + mBinding); Loading Loading @@ -3525,3 +3656,4 @@ public class BluetoothManagerService extends IBluetoothManager.Stub { return BluetoothAdapter.BT_SNOOP_LOG_MODE_DISABLED; } }
service/java/com/android/server/bluetooth/BluetoothSatelliteModeListener.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright 2023 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.server.bluetooth; import android.content.Context; import android.database.ContentObserver; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.provider.Settings; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; /** * The SatelliteModeListener handles system satellite mode change callback and inform * BluetoothManagerService on this change. */ public class BluetoothSatelliteModeListener { private static final String TAG = BluetoothSatelliteModeListener.class.getSimpleName(); private final BluetoothManagerService mBluetoothManagerService; private final BluetoothSatelliteModeHandler mHandler; private static final int MSG_SATELLITE_MODE_CHANGED = 0; BluetoothSatelliteModeListener(BluetoothManagerService service, Looper looper, Context context) { Log.d(TAG, " BluetoothSatelliteModeListener"); mBluetoothManagerService = service; mHandler = new BluetoothSatelliteModeHandler(looper); context.getContentResolver().registerContentObserver( Settings.Global.getUriFor(BluetoothManagerService.SETTINGS_SATELLITE_MODE_RADIOS), false, mSatelliteModeObserver); context.getContentResolver().registerContentObserver( Settings.Global.getUriFor(BluetoothManagerService.SETTINGS_SATELLITE_MODE_ENABLED), false, mSatelliteModeObserver); } private final ContentObserver mSatelliteModeObserver = new ContentObserver(null) { @Override public void onChange(boolean unused) { // Post from system main thread to android_io thread. mHandler.sendEmptyMessage(MSG_SATELLITE_MODE_CHANGED); } }; private class BluetoothSatelliteModeHandler extends Handler { BluetoothSatelliteModeHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { if (msg.what != MSG_SATELLITE_MODE_CHANGED) { Log.e(TAG, "Invalid message: " + msg.what); return; } handleSatelliteModeChange(); } } @VisibleForTesting public void handleSatelliteModeChange() { mBluetoothManagerService.onSatelliteModeChanged(); } }
service/tests/src/com/android/server/bluetooth/BluetoothSatelliteModeListenerTest.java 0 → 100644 +60 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.server.bluetooth; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.content.Context; import android.os.Looper; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class BluetoothSatelliteModeListenerTest { private BluetoothSatelliteModeListener mBluetoothSatelliteModeListener; @Mock private Context mContext; @Mock private ContentResolver mContentResolver; @Mock private BluetoothManagerService mBluetoothManagerService; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mContext.getContentResolver()).thenReturn(mContentResolver); mBluetoothSatelliteModeListener = new BluetoothSatelliteModeListener( mBluetoothManagerService, Looper.getMainLooper(), mContext); } @Test public void testHandleSatelliteModeChange_InvokeSatelliteModeChanged() { mBluetoothSatelliteModeListener.handleSatelliteModeChange(); verify(mBluetoothManagerService).onSatelliteModeChanged(); } }