Cleanup listener reference in Screen Attention

This will fix a memory leak being caused by dangling listeners.

Test: locally with oriole, make RunSettingsRoboTests -j$(nproc) ROBOTEST_FILTER=AdaptiveSleepCameraStatePreferenceControllerTest,AdaptiveSleepPreferenceControllerTest

Bug: 245990072
Change-Id: I35eeedc3ece719f1f3baff6235cc5ac2d42fbba3
This commit is contained in:
Abel Tesfaye
2022-09-09 21:14:43 +00:00
parent 88ea8b3bec
commit 378990e13f
3 changed files with 37 additions and 9 deletions

View File

@@ -19,9 +19,15 @@ package com.android.settings.display;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.hardware.SensorPrivacyManager.Sources.DIALOG; import static android.hardware.SensorPrivacyManager.Sources.DIALOG;
import static androidx.lifecycle.Lifecycle.Event.ON_START;
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
import android.content.Context; import android.content.Context;
import android.hardware.SensorPrivacyManager; import android.hardware.SensorPrivacyManager;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -32,17 +38,34 @@ import com.android.settingslib.widget.BannerMessagePreference;
* The controller of Screen attention's camera disabled warning preference. * The controller of Screen attention's camera disabled warning preference.
* The preference appears when the camera access is disabled for Screen Attention feature. * The preference appears when the camera access is disabled for Screen Attention feature.
*/ */
public class AdaptiveSleepCameraStatePreferenceController { public class AdaptiveSleepCameraStatePreferenceController implements LifecycleObserver {
@VisibleForTesting @VisibleForTesting
BannerMessagePreference mPreference; BannerMessagePreference mPreference;
private final SensorPrivacyManager mPrivacyManager; private final SensorPrivacyManager mPrivacyManager;
private final Context mContext; private final Context mContext;
public AdaptiveSleepCameraStatePreferenceController(Context context) { private final SensorPrivacyManager.OnSensorPrivacyChangedListener mPrivacyChangedListener =
new SensorPrivacyManager.OnSensorPrivacyChangedListener() {
@Override
public void onSensorPrivacyChanged(int sensor, boolean enabled) {
updateVisibility();
}
};
public AdaptiveSleepCameraStatePreferenceController(Context context, Lifecycle lifecycle) {
mPrivacyManager = SensorPrivacyManager.getInstance(context); mPrivacyManager = SensorPrivacyManager.getInstance(context);
mPrivacyManager.addSensorPrivacyListener(CAMERA,
(sensor, enabled) -> updateVisibility());
mContext = context; mContext = context;
lifecycle.addObserver(this);
}
@OnLifecycleEvent(ON_START)
public void onStart() {
mPrivacyManager.addSensorPrivacyListener(CAMERA, mPrivacyChangedListener);
}
@OnLifecycleEvent(ON_STOP)
public void onStop() {
mPrivacyManager.removeSensorPrivacyListener(CAMERA, mPrivacyChangedListener);
} }
/** /**
@@ -55,7 +78,7 @@ public class AdaptiveSleepCameraStatePreferenceController {
} }
/** /**
* Need this because all controller tests use RoboElectric. No easy way to mock this service, * Need this because all controller tests use Robolectric. No easy way to mock this service,
* so we mock the call we need * so we mock the call we need
*/ */
@VisibleForTesting @VisibleForTesting

View File

@@ -81,6 +81,7 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
}; };
private DevicePolicyManager mDevicePolicyManager; private DevicePolicyManager mDevicePolicyManager;
private SensorPrivacyManager.OnSensorPrivacyChangedListener mPrivacyChangedListener;
@VisibleForTesting @VisibleForTesting
Context mContext; Context mContext;
@@ -120,7 +121,7 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
mAdaptiveSleepPermissionController = new AdaptiveSleepPermissionPreferenceController( mAdaptiveSleepPermissionController = new AdaptiveSleepPermissionPreferenceController(
context); context);
mAdaptiveSleepCameraStatePreferenceController = mAdaptiveSleepCameraStatePreferenceController =
new AdaptiveSleepCameraStatePreferenceController(context); new AdaptiveSleepCameraStatePreferenceController(context, getLifecycle());
mAdaptiveSleepBatterySaverPreferenceController = mAdaptiveSleepBatterySaverPreferenceController =
new AdaptiveSleepBatterySaverPreferenceController(context); new AdaptiveSleepBatterySaverPreferenceController(context);
mPrivacyPreference = new FooterPreference(context); mPrivacyPreference = new FooterPreference(context);
@@ -129,8 +130,7 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
mPrivacyPreference.setSelectable(false); mPrivacyPreference.setSelectable(false);
mPrivacyPreference.setLayoutResource(R.layout.preference_footer); mPrivacyPreference.setLayoutResource(R.layout.preference_footer);
mPrivacyManager = SensorPrivacyManager.getInstance(context); mPrivacyManager = SensorPrivacyManager.getInstance(context);
mPrivacyManager.addSensorPrivacyListener(CAMERA, mPrivacyChangedListener = (sensor, enabled) -> mAdaptiveSleepController.updatePreference();
(sensor, enabled) -> mAdaptiveSleepController.updatePreference());
} }
@Override @Override
@@ -159,12 +159,14 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
mAdaptiveSleepController.updatePreference(); mAdaptiveSleepController.updatePreference();
mContext.registerReceiver(mReceiver, mContext.registerReceiver(mReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
mPrivacyManager.addSensorPrivacyListener(CAMERA, mPrivacyChangedListener);
} }
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
mContext.unregisterReceiver(mReceiver); mContext.unregisterReceiver(mReceiver);
mPrivacyManager.removeSensorPrivacyListener(CAMERA, mPrivacyChangedListener);
} }
@Override @Override

View File

@@ -28,6 +28,7 @@ import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import androidx.lifecycle.Lifecycle;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager; import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager;
@@ -50,6 +51,8 @@ public class AdaptiveSleepCameraStatePreferenceControllerTest {
private PackageManager mPackageManager; private PackageManager mPackageManager;
@Mock @Mock
private PreferenceScreen mScreen; private PreferenceScreen mScreen;
@Mock
private Lifecycle mLifecycle;
@Before @Before
public void setUp() { public void setUp() {
@@ -61,7 +64,7 @@ public class AdaptiveSleepCameraStatePreferenceControllerTest {
when(mPackageManager.checkPermission(any(), any())).thenReturn( when(mPackageManager.checkPermission(any(), any())).thenReturn(
PackageManager.PERMISSION_GRANTED); PackageManager.PERMISSION_GRANTED);
mController = new AdaptiveSleepCameraStatePreferenceController(mContext); mController = new AdaptiveSleepCameraStatePreferenceController(mContext, mLifecycle);
when(mController.isCameraLocked()).thenReturn(false); when(mController.isCameraLocked()).thenReturn(false);
} }