Add null check in ScanManager.downgradeScanModeFromMaxDuty
In function ScanManager.downgradeScanModeFromMaxDuty,
ScanClient.stats.setScanDowngrade is called while ScanClient.stats maybe
null. This is resulting in a NullPointerException. All other functions
inside ScanManager using ScanClient.stats perform a null check
beforehand, and therefore a null check is added to fix the NPE issue.
Unit test to check NPE fix is added as well.
Bug: 279848544
Test: atest BluetoothInstrumentationTests
Change-Id: I2dc50d15f7c45b2cc954c00b8ccc4f4313d7e6da
diff --git a/android/app/src/com/android/bluetooth/gatt/ScanManager.java b/android/app/src/com/android/bluetooth/gatt/ScanManager.java
index ec01343..e29117b 100644
--- a/android/app/src/com/android/bluetooth/gatt/ScanManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/ScanManager.java
@@ -829,7 +829,7 @@
}
private boolean downgradeScanModeFromMaxDuty(ScanClient client) {
- if (mAdapterService.getScanDowngradeDurationMillis() == 0) {
+ if ((client.stats == null) || mAdapterService.getScanDowngradeDurationMillis() == 0) {
return false;
}
if (ScanSettings.SCAN_MODE_LOW_LATENCY == client.settings.getScanMode()) {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java
index 4eb503e..9f42f3a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java
@@ -16,7 +16,6 @@
package com.android.bluetooth.gatt;
-import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_OPPORTUNISTIC;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
@@ -28,7 +27,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.anyString;
@@ -42,13 +40,9 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
-import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.le.ScanFilter;
-import android.bluetooth.le.ScanRecord;
-import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
-import android.bluetooth.le.TransportDiscoveryData;
import android.content.Context;
import android.location.LocationManager;
import android.os.Binder;
@@ -62,12 +56,10 @@
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;
-import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.BluetoothAdapterProxy;
import com.android.bluetooth.btservice.MetricsLogger;
-import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
import java.util.List;
@@ -75,7 +67,6 @@
import java.util.concurrent.TimeUnit;
import org.junit.After;
-import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -1236,4 +1227,52 @@
eq(BluetoothProtoEnums.SCREEN_OFF_EVENT), anyLong());
Mockito.clearInvocations(mMetricsLogger);
}
+
+ @Test
+ public void testDowngradeWithNonNullClientAppScanStats() {
+ // Set filtered scan flag
+ final boolean isFiltered = true;
+ // Set scan downgrade duration through Mock
+ when(mAdapterService.getScanDowngradeDurationMillis())
+ .thenReturn((long) DELAY_SCAN_DOWNGRADE_DURATION_MS);
+
+ // Turn off screen
+ sendMessageWaitForProcessed(createScreenOnOffMessage(false));
+ // Create scan client
+ ScanClient client = createScanClient(0, isFiltered, SCAN_MODE_LOW_LATENCY);
+ // Start Scan
+ sendMessageWaitForProcessed(createStartStopScanMessage(true, client));
+ assertThat(mScanManager.getRegularScanQueue().contains(client)).isTrue();
+ assertThat(mScanManager.getSuspendedScanQueue().contains(client)).isFalse();
+ assertThat(client.settings.getScanMode()).isEqualTo(SCAN_MODE_LOW_LATENCY);
+ // Set connecting state
+ sendMessageWaitForProcessed(createConnectingMessage(true));
+ // SCAN_MODE_LOW_LATENCY is now downgraded to SCAN_MODE_BALANCED
+ assertThat(client.settings.getScanMode()).isEqualTo(SCAN_MODE_BALANCED);
+ }
+
+ @Test
+ public void testDowngradeWithNullClientAppScanStats() {
+ // Set filtered scan flag
+ final boolean isFiltered = true;
+ // Set scan downgrade duration through Mock
+ when(mAdapterService.getScanDowngradeDurationMillis())
+ .thenReturn((long) DELAY_SCAN_DOWNGRADE_DURATION_MS);
+
+ // Turn off screen
+ sendMessageWaitForProcessed(createScreenOnOffMessage(false));
+ // Create scan client
+ ScanClient client = createScanClient(0, isFiltered, SCAN_MODE_LOW_LATENCY);
+ // Start Scan
+ sendMessageWaitForProcessed(createStartStopScanMessage(true, client));
+ assertThat(mScanManager.getRegularScanQueue().contains(client)).isTrue();
+ assertThat(mScanManager.getSuspendedScanQueue().contains(client)).isFalse();
+ assertThat(client.settings.getScanMode()).isEqualTo(SCAN_MODE_LOW_LATENCY);
+ // Set AppScanStats to null
+ client.stats = null;
+ // Set connecting state
+ sendMessageWaitForProcessed(createConnectingMessage(true));
+ // Since AppScanStats is null, no downgrade takes place for scan mode
+ assertThat(client.settings.getScanMode()).isEqualTo(SCAN_MODE_LOW_LATENCY);
+ }
}