Fix KeyguardSliceView leak
Tunable and slice listener was not being unregistered correctly.
Fixes: 150595377
Test: atest KeyguardSliceViewTest
Change-Id: I28afb56b732de2c3bffded031a6fc7731b72d36d
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index f48210c..95e9c20 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -29,6 +29,7 @@
import android.annotation.StyleRes;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.text.LineBreaker;
@@ -40,6 +41,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
+import android.view.Display;
import android.view.View;
import android.view.animation.Animation;
import android.widget.LinearLayout;
@@ -62,6 +64,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -90,6 +93,7 @@
private final ActivityStarter mActivityStarter;
private final ConfigurationController mConfigurationController;
private final LayoutTransition mLayoutTransition;
+ private final TunerService mTunerService;
private Uri mKeyguardSliceUri;
@VisibleForTesting
TextView mTitle;
@@ -114,16 +118,14 @@
@Inject
public KeyguardSliceView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- ActivityStarter activityStarter, ConfigurationController configurationController) {
+ ActivityStarter activityStarter, ConfigurationController configurationController,
+ TunerService tunerService, @Main Resources resources) {
super(context, attrs);
- TunerService tunerService = Dependency.get(TunerService.class);
- tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI);
-
+ mTunerService = tunerService;
mClickActions = new HashMap<>();
- mRowPadding = context.getResources().getDimensionPixelSize(R.dimen.subtitle_clock_padding);
- mRowWithHeaderPadding = context.getResources()
- .getDimensionPixelSize(R.dimen.header_subtitle_padding);
+ mRowPadding = resources.getDimensionPixelSize(R.dimen.subtitle_clock_padding);
+ mRowWithHeaderPadding = resources.getDimensionPixelSize(R.dimen.header_subtitle_padding);
mActivityStarter = activityStarter;
mConfigurationController = configurationController;
@@ -143,7 +145,8 @@
// Eventually the existing copy will be reparented instead, and we won't need this.
public KeyguardSliceView(Context context, AttributeSet attributeSet) {
this(context, attributeSet, Dependency.get(ActivityStarter.class),
- Dependency.get(ConfigurationController.class));
+ Dependency.get(ConfigurationController.class), Dependency.get(TunerService.class),
+ context.getResources());
}
@Override
@@ -166,9 +169,15 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mDisplayId = getDisplay().getDisplayId();
+ Display display = getDisplay();
+ if (display != null) {
+ mDisplayId = display.getDisplayId();
+ }
+ mTunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI);
// Make sure we always have the most current slice
- mLiveData.observeForever(this);
+ if (mDisplayId == DEFAULT_DISPLAY) {
+ mLiveData.observeForever(this);
+ }
mConfigurationController.addCallback(this);
}
@@ -180,6 +189,7 @@
if (mDisplayId == DEFAULT_DISPLAY) {
mLiveData.removeObserver(this);
}
+ mTunerService.removeTunable(this);
mConfigurationController.removeCallback(this);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 462b042..06552b9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -15,27 +15,38 @@
*/
package com.android.keyguard;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Color;
import android.net.Uri;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.AttributeSet;
import android.view.LayoutInflater;
+import android.view.View;
import androidx.slice.SliceProvider;
import androidx.slice.SliceSpecs;
import androidx.slice.builders.ListBuilder;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.KeyguardSliceProvider;
-import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.Collections;
import java.util.HashSet;
@@ -48,15 +59,40 @@
private KeyguardSliceView mKeyguardSliceView;
private Uri mSliceUri;
+ @Mock
+ private TunerService mTunerService;
+ @Mock
+ private ConfigurationController mConfigurationController;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private Resources mResources;
+
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
allowTestableLooperAsMainThread();
- InjectionInflationController inflationController = new InjectionInflationController(
- SystemUIFactory.getInstance().getRootComponent());
- LayoutInflater layoutInflater = inflationController
- .injectable(LayoutInflater.from(getContext()));
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+ layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
+
+ @Override
+ public View onCreateView(View parent, String name, Context context,
+ AttributeSet attrs) {
+ return onCreateView(name, context, attrs);
+ }
+
+ @Override
+ public View onCreateView(String name, Context context, AttributeSet attrs) {
+ if ("com.android.keyguard.KeyguardSliceView".equals(name)) {
+ return new KeyguardSliceView(getContext(), attrs, mActivityStarter,
+ mConfigurationController, mTunerService, mResources);
+ }
+ return null;
+ }
+ });
mKeyguardSliceView = (KeyguardSliceView) layoutInflater
.inflate(R.layout.keyguard_status_area, null);
+ mKeyguardSliceView.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
mSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
SliceProvider.setSpecs(new HashSet<>(Collections.singletonList(SliceSpecs.LIST)));
}
@@ -111,4 +147,18 @@
Assert.assertEquals("Should be using AOD text color", Color.WHITE,
mKeyguardSliceView.getTextColor());
}
+
+ @Test
+ public void onAttachedToWindow_registersListeners() {
+ mKeyguardSliceView.onAttachedToWindow();
+ verify(mTunerService).addTunable(eq(mKeyguardSliceView), anyString());
+ verify(mConfigurationController).addCallback(eq(mKeyguardSliceView));
+ }
+
+ @Test
+ public void onDetachedFromWindow_unregistersListeners() {
+ mKeyguardSliceView.onDetachedFromWindow();
+ verify(mTunerService).removeTunable(eq(mKeyguardSliceView));
+ verify(mConfigurationController).removeCallback(eq(mKeyguardSliceView));
+ }
}