Loading android/app/src/com/android/bluetooth/btservice/AdapterService.java +18 −0 Original line number Diff line number Diff line Loading @@ -5298,6 +5298,9 @@ public class AdapterService extends Service { private int mScanUpgradeDurationMillis = DeviceConfigListener.DEFAULT_SCAN_UPGRADE_DURATION_MILLIS; @GuardedBy("mDeviceConfigLock") private int mScanDowngradeDurationMillis = DeviceConfigListener.DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS; @GuardedBy("mDeviceConfigLock") private int mScreenOffLowPowerWindowMillis = ScanManager.SCAN_MODE_SCREEN_OFF_LOW_POWER_WINDOW_MS; @GuardedBy("mDeviceConfigLock") Loading Loading @@ -5364,6 +5367,15 @@ public class AdapterService extends Service { } } /** * Returns scan downgrade duration in millis. */ public long getScanDowngradeDurationMillis() { synchronized (mDeviceConfigLock) { return mScanDowngradeDurationMillis; } } /** * Returns SCREEN_OFF_BALANCED scan window in millis. */ Loading Loading @@ -5417,6 +5429,8 @@ public class AdapterService extends Service { "scan_timeout_millis"; private static final String SCAN_UPGRADE_DURATION_MILLIS = "scan_upgrade_duration_millis"; private static final String SCAN_DOWNGRADE_DURATION_MILLIS = "scan_downgrade_duration_millis"; private static final String SCREEN_OFF_LOW_POWER_WINDOW_MILLIS = "screen_off_low_power_window_millis"; private static final String SCREEN_OFF_LOW_POWER_INTERVAL_MILLIS = Loading @@ -5436,6 +5450,8 @@ public class AdapterService extends Service { private static final long DEFAULT_SCAN_QUOTA_WINDOW_MILLIS = 30 * SECOND_IN_MILLIS; private static final long DEFAULT_SCAN_TIMEOUT_MILLIS = 30 * MINUTE_IN_MILLIS; private static final int DEFAULT_SCAN_UPGRADE_DURATION_MILLIS = (int) SECOND_IN_MILLIS * 6; private static final int DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS = (int) SECOND_IN_MILLIS * 6; @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public void start() { Loading Loading @@ -5464,6 +5480,8 @@ public class AdapterService extends Service { DEFAULT_SCAN_TIMEOUT_MILLIS); mScanUpgradeDurationMillis = properties.getInt(SCAN_UPGRADE_DURATION_MILLIS, DEFAULT_SCAN_UPGRADE_DURATION_MILLIS); mScanDowngradeDurationMillis = properties.getInt(SCAN_DOWNGRADE_DURATION_MILLIS, DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS); mScreenOffLowPowerWindowMillis = properties.getInt( SCREEN_OFF_LOW_POWER_WINDOW_MILLIS, ScanManager.SCAN_MODE_SCREEN_OFF_LOW_POWER_WINDOW_MS); Loading android/app/src/com/android/bluetooth/gatt/AppScanStats.java +24 −3 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ import java.util.Objects; public long timestamp; public boolean isOpportunisticScan; public boolean isTimeout; public boolean isDowngraded; public boolean isBackgroundScan; public boolean isFilterScan; public boolean isCallbackScan; Loading @@ -91,6 +92,7 @@ import java.util.Objects; this.timestamp = timestamp; this.isOpportunisticScan = false; this.isTimeout = false; this.isDowngraded = false; this.isBackgroundScan = false; this.isFilterScan = isFilterScan; this.isCallbackScan = isCallbackScan; Loading Loading @@ -172,11 +174,19 @@ import java.util.Objects; } synchronized boolean isScanTimeout(int scannerId) { LastScan onGoingScan = getScanFromScannerId(scannerId); if (onGoingScan == null) { LastScan scan = getScanFromScannerId(scannerId); if (scan == null) { return false; } return onGoingScan.isTimeout; return scan.isTimeout; } synchronized boolean isScanDowngraded(int scannerId) { LastScan scan = getScanFromScannerId(scannerId); if (scan == null) { return false; } return scan.isDowngraded; } synchronized void recordScanStart(ScanSettings settings, List<ScanFilter> filters, Loading Loading @@ -342,6 +352,17 @@ import java.util.Objects; } } synchronized void setScanDowngrade(int scannerId, boolean isDowngrade) { if (!isScanning()) { return; } LastScan scan = getScanFromScannerId(scannerId); if (scan != null) { scan.isDowngraded = isDowngrade; } } synchronized boolean isScanningTooFrequently() { if (mLastScans.size() < mAdapterService.getScanQuotaCount()) { return false; Loading android/app/src/com/android/bluetooth/gatt/ScanManager.java +245 −7 Original line number Diff line number Diff line Loading @@ -20,6 +20,13 @@ import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.PendingIntent; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanSettings; Loading Loading @@ -100,6 +107,8 @@ public class ScanManager { static final int MSG_SCREEN_ON = 7; static final int MSG_SCREEN_OFF = 8; static final int MSG_REVERT_SCAN_MODE_UPGRADE = 9; static final int MSG_START_CONNECTING = 10; static final int MSG_STOP_CONNECTING = 11; private static final String ACTION_REFRESH_BATCHED_SCAN = "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN"; Loading Loading @@ -139,6 +148,8 @@ public class ScanManager { private static final int SCAN_MODE_APP_IN_BACKGROUND = ScanSettings.SCAN_MODE_LOW_POWER; private final SparseBooleanArray mIsUidForegroundMap = new SparseBooleanArray(); private boolean mScreenOn = false; private boolean mIsConnecting; private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting; @VisibleForTesting static class UidImportance { Loading @@ -165,6 +176,7 @@ public class ScanManager { mLocationManager = mService.getSystemService(LocationManager.class); mAdapterService = adapterService; mBluetoothAdapterProxy = bluetoothAdapterProxy; mIsConnecting = false; mPriorityMap.put(ScanSettings.SCAN_MODE_OPPORTUNISTIC, 0); mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF, 1); Loading @@ -190,6 +202,8 @@ public class ScanManager { } IntentFilter locationIntentFilter = new IntentFilter(LocationManager.MODE_CHANGED_ACTION); mService.registerReceiver(mLocationReceiver, locationIntentFilter); mService.registerReceiver(mBluetoothConnectionReceiver, getBluetoothConnectionIntentFilter()); } void cleanup() { Loading Loading @@ -225,6 +239,13 @@ public class ScanManager { } catch (IllegalArgumentException e) { Log.w(TAG, "exception when invoking unregisterReceiver(mLocationReceiver)", e); } try { mService.unregisterReceiver(mBluetoothConnectionReceiver); } catch (IllegalArgumentException e) { Log.w(TAG, "exception when invoking unregisterReceiver(mBluetoothConnectionReceiver)", e); } } void registerScanner(UUID uuid) { Loading Loading @@ -301,6 +322,25 @@ public class ScanManager { // TODO: add a callback for scan failure. } void onConnectingState(boolean isConnecting) { if (isConnecting) { sendMessage(MSG_START_CONNECTING, null); } else { sendMessage(MSG_STOP_CONNECTING, null); } } private IntentFilter getBluetoothConnectionIntentFilter() { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); return filter; } private void sendMessage(int what, ScanClient client) { final ClientHandler handler = mHandler; if (handler == null) { Loading Loading @@ -361,6 +401,12 @@ public class ScanManager { case MSG_IMPORTANCE_CHANGE: handleImportanceChange((UidImportance) msg.obj); break; case MSG_START_CONNECTING: handleConnectingState(); break; case MSG_STOP_CONNECTING: handleClearConnectingState(); break; default: // Shouldn't happen. Log.e(TAG, "received an unkown message : " + msg.what); Loading Loading @@ -409,12 +455,13 @@ public class ScanManager { mScanNative.startBatchScan(client); } else { updateScanModeBeforeStart(client); updateScanModeConcurrency(client); mRegularScanClients.add(client); mScanNative.startRegularScan(client); if (!mScanNative.isOpportunisticScanClient(client)) { mScanNative.configureRegularScanParams(); if (!mScanNative.isExemptFromScanDowngrade(client)) { if (!mScanNative.isExemptFromScanTimeout(client)) { Message msg = obtainMessage(MSG_SCAN_TIMEOUT); msg.obj = client; // Only one timeout message should exist at any time Loading Loading @@ -511,6 +558,55 @@ public class ScanManager { updateRegularScanClientsScreenOff(); } void handleConnectingState() { if (mAdapterService.getScanDowngradeDurationMillis() == 0) { return; } boolean updatedScanParams = false; mIsConnecting = true; if (DBG) { Log.d(TAG, "handleConnectingState()"); } for (ScanClient client : mRegularScanClients) { if (downgradeScanModeFromMaxDuty(client)) { updatedScanParams = true; if (DBG) { Log.d(TAG, "scanMode is downgraded by connecting for " + client); } } } if (updatedScanParams) { mScanNative.configureRegularScanParams(); } removeMessages(MSG_STOP_CONNECTING); Message msg = obtainMessage(MSG_STOP_CONNECTING); sendMessageDelayed(msg, mAdapterService.getScanDowngradeDurationMillis()); } void handleClearConnectingState() { if (!mIsConnecting) { Log.e(TAG, "handleClearConnectingState() - not connecting state"); return; } if (DBG) { Log.d(TAG, "handleClearConnectingState()"); } boolean updatedScanParams = false; for (ScanClient client : mRegularScanClients) { if (revertDowngradeScanModeFromMaxDuty(client)) { updatedScanParams = true; if (DBG) { Log.d(TAG, "downgraded scanMode is reverted for " + client); } } } if (updatedScanParams) { mScanNative.configureRegularScanParams(); } removeMessages(MSG_STOP_CONNECTING); mIsConnecting = false; } @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) void handleSuspendScans() { for (ScanClient client : mRegularScanClients) { Loading Loading @@ -545,7 +641,7 @@ public class ScanManager { } private boolean updateScanModeScreenOff(ScanClient client) { if (mScanNative.isTimeoutScanClient(client)) { if (mScanNative.isForceDowngradedScanClient(client)) { return false; } if (!isAppForeground(client) && !mScanNative.isOpportunisticScanClient(client)) { Loading @@ -560,8 +656,9 @@ public class ScanManager { case ScanSettings.SCAN_MODE_BALANCED: case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED); case ScanSettings.SCAN_MODE_OPPORTUNISTIC: case ScanSettings.SCAN_MODE_LOW_LATENCY: return client.updateScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); case ScanSettings.SCAN_MODE_OPPORTUNISTIC: default: return false; } Loading @@ -586,6 +683,13 @@ public class ScanManager { } } private boolean updateScanModeConcurrency(ScanClient client) { if (mIsConnecting) { return downgradeScanModeFromMaxDuty(client); } return false; } private boolean upgradeScanModeBeforeStart(ScanClient client) { if (client.started || mAdapterService.getScanUpgradeDurationMillis() == 0) { return false; Loading Loading @@ -637,7 +741,7 @@ public class ScanManager { } private boolean updateScanModeScreenOn(ScanClient client) { if (mScanNative.isTimeoutScanClient(client)) { if (mScanNative.isForceDowngradedScanClient(client)) { return false; } Loading @@ -647,6 +751,36 @@ public class ScanManager { return client.updateScanMode(newScanMode); } private boolean downgradeScanModeFromMaxDuty(ScanClient client) { if (mAdapterService.getScanDowngradeDurationMillis() == 0) { return false; } if (ScanSettings.SCAN_MODE_LOW_LATENCY == client.settings.getScanMode()) { client.updateScanMode(ScanSettings.SCAN_MODE_BALANCED); client.stats.setScanDowngrade(client.scannerId, true); if (DBG) { Log.d(TAG, "downgradeScanModeFromMaxDuty() for " + client); } return true; } return false; } private boolean revertDowngradeScanModeFromMaxDuty(ScanClient client) { if (!mScanNative.isDowngradedScanClient(client)) { return false; } client.stats.setScanDowngrade(client.scannerId, false); if (DBG) { Log.d(TAG, "revertDowngradeScanModeFromMaxDuty() for " + client); } if (mScreenOn) { return updateScanModeScreenOn(client); } else { return updateScanModeScreenOff(client); } } void handleScreenOn() { if (mScreenOn) { return; Loading Loading @@ -900,7 +1034,7 @@ public class ScanManager { } } private boolean isExemptFromScanDowngrade(ScanClient client) { private boolean isExemptFromScanTimeout(ScanClient client) { return isOpportunisticScanClient(client) || isFirstMatchScanClient(client); } Loading @@ -912,6 +1046,14 @@ public class ScanManager { return (client.stats != null) && client.stats.isScanTimeout(client.scannerId); } private boolean isDowngradedScanClient(ScanClient client) { return (client.stats != null) && client.stats.isScanDowngraded(client.scannerId); } private boolean isForceDowngradedScanClient(ScanClient client) { return isTimeoutScanClient(client) || isDowngradedScanClient(client); } private boolean isFirstMatchScanClient(ScanClient client) { return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0; Loading Loading @@ -1078,7 +1220,7 @@ public class ScanManager { } void regularScanTimeout(ScanClient client) { if (!isExemptFromScanDowngrade(client) && client.stats.isScanningTooLong()) { if (!isExemptFromScanTimeout(client) && client.stats.isScanningTooLong()) { if (DBG) { Log.d(TAG, "regularScanTimeout - client scan time was too long"); } Loading Loading @@ -1640,6 +1782,102 @@ public class ScanManager { } }; private BroadcastReceiver mBluetoothConnectionReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action == null) { Log.w(TAG, "Received intent with null action"); return; } switch (action) { case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED: case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED: case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: case BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED: case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED: int prevState = intent.getIntExtra( BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); if (DBG) { Log.d(TAG, "PROFILE_CONNECTION_STATE_CHANGE: action=" + action + ", prevState=" + prevState + ", state=" + state); } boolean updatedConnectingState = updateCountersAndCheckForConnectingState(state, prevState); if (DBG) { Log.d(TAG, "updatedConnectingState = " + updatedConnectingState); } if (updatedConnectingState) { if (!mIsConnecting) { onConnectingState(true); } } else { if (mIsConnecting) { onConnectingState(false); } } break; default: Log.w(TAG, "Received unknown intent " + intent); break; } } }; private boolean updateCountersAndCheckForConnectingState(int state, int prevState) { switch (prevState) { case BluetoothProfile.STATE_CONNECTING: if (mProfilesConnecting > 0) { mProfilesConnecting--; } else { Log.e(TAG, "mProfilesConnecting " + mProfilesConnecting); throw new IllegalStateException( "Invalid state transition, " + prevState + " -> " + state); } break; case BluetoothProfile.STATE_CONNECTED: if (mProfilesConnected > 0) { mProfilesConnected--; } else { Log.e(TAG, "mProfilesConnected " + mProfilesConnected); throw new IllegalStateException( "Invalid state transition, " + prevState + " -> " + state); } break; case BluetoothProfile.STATE_DISCONNECTING: if (mProfilesDisconnecting > 0) { mProfilesDisconnecting--; } else { Log.e(TAG, "mProfilesDisconnecting " + mProfilesDisconnecting); throw new IllegalStateException( "Invalid state transition, " + prevState + " -> " + state); } break; } switch (state) { case BluetoothProfile.STATE_CONNECTING: mProfilesConnecting++; break; case BluetoothProfile.STATE_CONNECTED: mProfilesConnected++; break; case BluetoothProfile.STATE_DISCONNECTING: mProfilesDisconnecting++; break; case BluetoothProfile.STATE_DISCONNECTED: break; default: } if (DBG) { Log.d(TAG, "mProfilesConnecting " + mProfilesConnecting + ", mProfilesConnected " + mProfilesConnected + ", mProfilesDisconnecting " + mProfilesDisconnecting); } return (mProfilesConnecting > 0); } private void handleImportanceChange(UidImportance imp) { if (imp == null) { return; Loading @@ -1655,7 +1893,7 @@ public class ScanManager { } for (ScanClient client : mRegularScanClients) { if (client.appUid != uid || mScanNative.isTimeoutScanClient(client)) { if (client.appUid != uid || mScanNative.isForceDowngradedScanClient(client)) { continue; } if (isForeground) { Loading android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java +217 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
android/app/src/com/android/bluetooth/btservice/AdapterService.java +18 −0 Original line number Diff line number Diff line Loading @@ -5298,6 +5298,9 @@ public class AdapterService extends Service { private int mScanUpgradeDurationMillis = DeviceConfigListener.DEFAULT_SCAN_UPGRADE_DURATION_MILLIS; @GuardedBy("mDeviceConfigLock") private int mScanDowngradeDurationMillis = DeviceConfigListener.DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS; @GuardedBy("mDeviceConfigLock") private int mScreenOffLowPowerWindowMillis = ScanManager.SCAN_MODE_SCREEN_OFF_LOW_POWER_WINDOW_MS; @GuardedBy("mDeviceConfigLock") Loading Loading @@ -5364,6 +5367,15 @@ public class AdapterService extends Service { } } /** * Returns scan downgrade duration in millis. */ public long getScanDowngradeDurationMillis() { synchronized (mDeviceConfigLock) { return mScanDowngradeDurationMillis; } } /** * Returns SCREEN_OFF_BALANCED scan window in millis. */ Loading Loading @@ -5417,6 +5429,8 @@ public class AdapterService extends Service { "scan_timeout_millis"; private static final String SCAN_UPGRADE_DURATION_MILLIS = "scan_upgrade_duration_millis"; private static final String SCAN_DOWNGRADE_DURATION_MILLIS = "scan_downgrade_duration_millis"; private static final String SCREEN_OFF_LOW_POWER_WINDOW_MILLIS = "screen_off_low_power_window_millis"; private static final String SCREEN_OFF_LOW_POWER_INTERVAL_MILLIS = Loading @@ -5436,6 +5450,8 @@ public class AdapterService extends Service { private static final long DEFAULT_SCAN_QUOTA_WINDOW_MILLIS = 30 * SECOND_IN_MILLIS; private static final long DEFAULT_SCAN_TIMEOUT_MILLIS = 30 * MINUTE_IN_MILLIS; private static final int DEFAULT_SCAN_UPGRADE_DURATION_MILLIS = (int) SECOND_IN_MILLIS * 6; private static final int DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS = (int) SECOND_IN_MILLIS * 6; @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public void start() { Loading Loading @@ -5464,6 +5480,8 @@ public class AdapterService extends Service { DEFAULT_SCAN_TIMEOUT_MILLIS); mScanUpgradeDurationMillis = properties.getInt(SCAN_UPGRADE_DURATION_MILLIS, DEFAULT_SCAN_UPGRADE_DURATION_MILLIS); mScanDowngradeDurationMillis = properties.getInt(SCAN_DOWNGRADE_DURATION_MILLIS, DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS); mScreenOffLowPowerWindowMillis = properties.getInt( SCREEN_OFF_LOW_POWER_WINDOW_MILLIS, ScanManager.SCAN_MODE_SCREEN_OFF_LOW_POWER_WINDOW_MS); Loading
android/app/src/com/android/bluetooth/gatt/AppScanStats.java +24 −3 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ import java.util.Objects; public long timestamp; public boolean isOpportunisticScan; public boolean isTimeout; public boolean isDowngraded; public boolean isBackgroundScan; public boolean isFilterScan; public boolean isCallbackScan; Loading @@ -91,6 +92,7 @@ import java.util.Objects; this.timestamp = timestamp; this.isOpportunisticScan = false; this.isTimeout = false; this.isDowngraded = false; this.isBackgroundScan = false; this.isFilterScan = isFilterScan; this.isCallbackScan = isCallbackScan; Loading Loading @@ -172,11 +174,19 @@ import java.util.Objects; } synchronized boolean isScanTimeout(int scannerId) { LastScan onGoingScan = getScanFromScannerId(scannerId); if (onGoingScan == null) { LastScan scan = getScanFromScannerId(scannerId); if (scan == null) { return false; } return onGoingScan.isTimeout; return scan.isTimeout; } synchronized boolean isScanDowngraded(int scannerId) { LastScan scan = getScanFromScannerId(scannerId); if (scan == null) { return false; } return scan.isDowngraded; } synchronized void recordScanStart(ScanSettings settings, List<ScanFilter> filters, Loading Loading @@ -342,6 +352,17 @@ import java.util.Objects; } } synchronized void setScanDowngrade(int scannerId, boolean isDowngrade) { if (!isScanning()) { return; } LastScan scan = getScanFromScannerId(scannerId); if (scan != null) { scan.isDowngraded = isDowngrade; } } synchronized boolean isScanningTooFrequently() { if (mLastScans.size() < mAdapterService.getScanQuotaCount()) { return false; Loading
android/app/src/com/android/bluetooth/gatt/ScanManager.java +245 −7 Original line number Diff line number Diff line Loading @@ -20,6 +20,13 @@ import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.PendingIntent; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanSettings; Loading Loading @@ -100,6 +107,8 @@ public class ScanManager { static final int MSG_SCREEN_ON = 7; static final int MSG_SCREEN_OFF = 8; static final int MSG_REVERT_SCAN_MODE_UPGRADE = 9; static final int MSG_START_CONNECTING = 10; static final int MSG_STOP_CONNECTING = 11; private static final String ACTION_REFRESH_BATCHED_SCAN = "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN"; Loading Loading @@ -139,6 +148,8 @@ public class ScanManager { private static final int SCAN_MODE_APP_IN_BACKGROUND = ScanSettings.SCAN_MODE_LOW_POWER; private final SparseBooleanArray mIsUidForegroundMap = new SparseBooleanArray(); private boolean mScreenOn = false; private boolean mIsConnecting; private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting; @VisibleForTesting static class UidImportance { Loading @@ -165,6 +176,7 @@ public class ScanManager { mLocationManager = mService.getSystemService(LocationManager.class); mAdapterService = adapterService; mBluetoothAdapterProxy = bluetoothAdapterProxy; mIsConnecting = false; mPriorityMap.put(ScanSettings.SCAN_MODE_OPPORTUNISTIC, 0); mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF, 1); Loading @@ -190,6 +202,8 @@ public class ScanManager { } IntentFilter locationIntentFilter = new IntentFilter(LocationManager.MODE_CHANGED_ACTION); mService.registerReceiver(mLocationReceiver, locationIntentFilter); mService.registerReceiver(mBluetoothConnectionReceiver, getBluetoothConnectionIntentFilter()); } void cleanup() { Loading Loading @@ -225,6 +239,13 @@ public class ScanManager { } catch (IllegalArgumentException e) { Log.w(TAG, "exception when invoking unregisterReceiver(mLocationReceiver)", e); } try { mService.unregisterReceiver(mBluetoothConnectionReceiver); } catch (IllegalArgumentException e) { Log.w(TAG, "exception when invoking unregisterReceiver(mBluetoothConnectionReceiver)", e); } } void registerScanner(UUID uuid) { Loading Loading @@ -301,6 +322,25 @@ public class ScanManager { // TODO: add a callback for scan failure. } void onConnectingState(boolean isConnecting) { if (isConnecting) { sendMessage(MSG_START_CONNECTING, null); } else { sendMessage(MSG_STOP_CONNECTING, null); } } private IntentFilter getBluetoothConnectionIntentFilter() { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); return filter; } private void sendMessage(int what, ScanClient client) { final ClientHandler handler = mHandler; if (handler == null) { Loading Loading @@ -361,6 +401,12 @@ public class ScanManager { case MSG_IMPORTANCE_CHANGE: handleImportanceChange((UidImportance) msg.obj); break; case MSG_START_CONNECTING: handleConnectingState(); break; case MSG_STOP_CONNECTING: handleClearConnectingState(); break; default: // Shouldn't happen. Log.e(TAG, "received an unkown message : " + msg.what); Loading Loading @@ -409,12 +455,13 @@ public class ScanManager { mScanNative.startBatchScan(client); } else { updateScanModeBeforeStart(client); updateScanModeConcurrency(client); mRegularScanClients.add(client); mScanNative.startRegularScan(client); if (!mScanNative.isOpportunisticScanClient(client)) { mScanNative.configureRegularScanParams(); if (!mScanNative.isExemptFromScanDowngrade(client)) { if (!mScanNative.isExemptFromScanTimeout(client)) { Message msg = obtainMessage(MSG_SCAN_TIMEOUT); msg.obj = client; // Only one timeout message should exist at any time Loading Loading @@ -511,6 +558,55 @@ public class ScanManager { updateRegularScanClientsScreenOff(); } void handleConnectingState() { if (mAdapterService.getScanDowngradeDurationMillis() == 0) { return; } boolean updatedScanParams = false; mIsConnecting = true; if (DBG) { Log.d(TAG, "handleConnectingState()"); } for (ScanClient client : mRegularScanClients) { if (downgradeScanModeFromMaxDuty(client)) { updatedScanParams = true; if (DBG) { Log.d(TAG, "scanMode is downgraded by connecting for " + client); } } } if (updatedScanParams) { mScanNative.configureRegularScanParams(); } removeMessages(MSG_STOP_CONNECTING); Message msg = obtainMessage(MSG_STOP_CONNECTING); sendMessageDelayed(msg, mAdapterService.getScanDowngradeDurationMillis()); } void handleClearConnectingState() { if (!mIsConnecting) { Log.e(TAG, "handleClearConnectingState() - not connecting state"); return; } if (DBG) { Log.d(TAG, "handleClearConnectingState()"); } boolean updatedScanParams = false; for (ScanClient client : mRegularScanClients) { if (revertDowngradeScanModeFromMaxDuty(client)) { updatedScanParams = true; if (DBG) { Log.d(TAG, "downgraded scanMode is reverted for " + client); } } } if (updatedScanParams) { mScanNative.configureRegularScanParams(); } removeMessages(MSG_STOP_CONNECTING); mIsConnecting = false; } @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) void handleSuspendScans() { for (ScanClient client : mRegularScanClients) { Loading Loading @@ -545,7 +641,7 @@ public class ScanManager { } private boolean updateScanModeScreenOff(ScanClient client) { if (mScanNative.isTimeoutScanClient(client)) { if (mScanNative.isForceDowngradedScanClient(client)) { return false; } if (!isAppForeground(client) && !mScanNative.isOpportunisticScanClient(client)) { Loading @@ -560,8 +656,9 @@ public class ScanManager { case ScanSettings.SCAN_MODE_BALANCED: case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED); case ScanSettings.SCAN_MODE_OPPORTUNISTIC: case ScanSettings.SCAN_MODE_LOW_LATENCY: return client.updateScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); case ScanSettings.SCAN_MODE_OPPORTUNISTIC: default: return false; } Loading @@ -586,6 +683,13 @@ public class ScanManager { } } private boolean updateScanModeConcurrency(ScanClient client) { if (mIsConnecting) { return downgradeScanModeFromMaxDuty(client); } return false; } private boolean upgradeScanModeBeforeStart(ScanClient client) { if (client.started || mAdapterService.getScanUpgradeDurationMillis() == 0) { return false; Loading Loading @@ -637,7 +741,7 @@ public class ScanManager { } private boolean updateScanModeScreenOn(ScanClient client) { if (mScanNative.isTimeoutScanClient(client)) { if (mScanNative.isForceDowngradedScanClient(client)) { return false; } Loading @@ -647,6 +751,36 @@ public class ScanManager { return client.updateScanMode(newScanMode); } private boolean downgradeScanModeFromMaxDuty(ScanClient client) { if (mAdapterService.getScanDowngradeDurationMillis() == 0) { return false; } if (ScanSettings.SCAN_MODE_LOW_LATENCY == client.settings.getScanMode()) { client.updateScanMode(ScanSettings.SCAN_MODE_BALANCED); client.stats.setScanDowngrade(client.scannerId, true); if (DBG) { Log.d(TAG, "downgradeScanModeFromMaxDuty() for " + client); } return true; } return false; } private boolean revertDowngradeScanModeFromMaxDuty(ScanClient client) { if (!mScanNative.isDowngradedScanClient(client)) { return false; } client.stats.setScanDowngrade(client.scannerId, false); if (DBG) { Log.d(TAG, "revertDowngradeScanModeFromMaxDuty() for " + client); } if (mScreenOn) { return updateScanModeScreenOn(client); } else { return updateScanModeScreenOff(client); } } void handleScreenOn() { if (mScreenOn) { return; Loading Loading @@ -900,7 +1034,7 @@ public class ScanManager { } } private boolean isExemptFromScanDowngrade(ScanClient client) { private boolean isExemptFromScanTimeout(ScanClient client) { return isOpportunisticScanClient(client) || isFirstMatchScanClient(client); } Loading @@ -912,6 +1046,14 @@ public class ScanManager { return (client.stats != null) && client.stats.isScanTimeout(client.scannerId); } private boolean isDowngradedScanClient(ScanClient client) { return (client.stats != null) && client.stats.isScanDowngraded(client.scannerId); } private boolean isForceDowngradedScanClient(ScanClient client) { return isTimeoutScanClient(client) || isDowngradedScanClient(client); } private boolean isFirstMatchScanClient(ScanClient client) { return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0; Loading Loading @@ -1078,7 +1220,7 @@ public class ScanManager { } void regularScanTimeout(ScanClient client) { if (!isExemptFromScanDowngrade(client) && client.stats.isScanningTooLong()) { if (!isExemptFromScanTimeout(client) && client.stats.isScanningTooLong()) { if (DBG) { Log.d(TAG, "regularScanTimeout - client scan time was too long"); } Loading Loading @@ -1640,6 +1782,102 @@ public class ScanManager { } }; private BroadcastReceiver mBluetoothConnectionReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action == null) { Log.w(TAG, "Received intent with null action"); return; } switch (action) { case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED: case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED: case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: case BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED: case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED: int prevState = intent.getIntExtra( BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); if (DBG) { Log.d(TAG, "PROFILE_CONNECTION_STATE_CHANGE: action=" + action + ", prevState=" + prevState + ", state=" + state); } boolean updatedConnectingState = updateCountersAndCheckForConnectingState(state, prevState); if (DBG) { Log.d(TAG, "updatedConnectingState = " + updatedConnectingState); } if (updatedConnectingState) { if (!mIsConnecting) { onConnectingState(true); } } else { if (mIsConnecting) { onConnectingState(false); } } break; default: Log.w(TAG, "Received unknown intent " + intent); break; } } }; private boolean updateCountersAndCheckForConnectingState(int state, int prevState) { switch (prevState) { case BluetoothProfile.STATE_CONNECTING: if (mProfilesConnecting > 0) { mProfilesConnecting--; } else { Log.e(TAG, "mProfilesConnecting " + mProfilesConnecting); throw new IllegalStateException( "Invalid state transition, " + prevState + " -> " + state); } break; case BluetoothProfile.STATE_CONNECTED: if (mProfilesConnected > 0) { mProfilesConnected--; } else { Log.e(TAG, "mProfilesConnected " + mProfilesConnected); throw new IllegalStateException( "Invalid state transition, " + prevState + " -> " + state); } break; case BluetoothProfile.STATE_DISCONNECTING: if (mProfilesDisconnecting > 0) { mProfilesDisconnecting--; } else { Log.e(TAG, "mProfilesDisconnecting " + mProfilesDisconnecting); throw new IllegalStateException( "Invalid state transition, " + prevState + " -> " + state); } break; } switch (state) { case BluetoothProfile.STATE_CONNECTING: mProfilesConnecting++; break; case BluetoothProfile.STATE_CONNECTED: mProfilesConnected++; break; case BluetoothProfile.STATE_DISCONNECTING: mProfilesDisconnecting++; break; case BluetoothProfile.STATE_DISCONNECTED: break; default: } if (DBG) { Log.d(TAG, "mProfilesConnecting " + mProfilesConnecting + ", mProfilesConnected " + mProfilesConnected + ", mProfilesDisconnecting " + mProfilesDisconnecting); } return (mProfilesConnecting > 0); } private void handleImportanceChange(UidImportance imp) { if (imp == null) { return; Loading @@ -1655,7 +1893,7 @@ public class ScanManager { } for (ScanClient client : mRegularScanClients) { if (client.appUid != uid || mScanNative.isTimeoutScanClient(client)) { if (client.appUid != uid || mScanNative.isForceDowngradedScanClient(client)) { continue; } if (isForeground) { Loading
android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java +217 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes