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));
+    }
 }