From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabi=C3=A1n=20Kozynski?= <kozynski@google.com>
Date: Fri, 13 Oct 2023 16:19:27 -0400
Subject: [PATCH] Unbind TileService onNullBinding

Test: atest TileLifecycleManagerTest
Test: manual: adb shell dumpsys activity service
Test: sts test
Bug: 300903792
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:7bf830ca0df71496cd47563e138b8712918e0476)
Merged-In: Ia8126ac65432b124683960e3ebf47301ba6172a1
Change-Id: Ia8126ac65432b124683960e3ebf47301ba6172a1
---
 .../qs/external/TileLifecycleManager.java     |  5 +++
 .../qs/external/TileLifecycleManagerTest.java | 33 ++++++++++++++++---
 2 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index effea6a877b8..270b7dd9d936 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -194,6 +194,11 @@ public class TileLifecycleManager extends BroadcastReceiver implements
         handlePendingMessages();
     }
 
+    @Override
+    public void onNullBinding(ComponentName name) {
+        setBindService(false);
+    }
+
     @Override
     public void onServiceDisconnected(ComponentName name) {
         if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index f35295cf6f99..329af0f1fb57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -22,13 +22,16 @@ import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.PackageInfo;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
@@ -50,7 +53,7 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
+import org.mockito.ArgumentCaptor;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -58,8 +61,8 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
     private static final int TEST_FAIL_TIMEOUT = 5000;
 
     private final PackageManagerAdapter mMockPackageManagerAdapter =
-            Mockito.mock(PackageManagerAdapter.class);
-    private final IQSTileService.Stub mMockTileService = Mockito.mock(IQSTileService.Stub.class);
+            mock(PackageManagerAdapter.class);
+    private final IQSTileService.Stub mMockTileService = mock(IQSTileService.Stub.class);
     private ComponentName mTileServiceComponentName;
     private Intent mTileServiceIntent;
     private UserHandle mUser;
@@ -84,7 +87,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
         mThread.start();
         mHandler = Handler.createAsync(mThread.getLooper());
         mStateManager = new TileLifecycleManager(mHandler, mContext,
-                Mockito.mock(IQSService.class), new Tile(),
+                mock(IQSService.class), new Tile(),
                 mTileServiceIntent,
                 mUser,
                 mMockPackageManagerAdapter);
@@ -237,4 +240,26 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
         verifyBind(2);
         verify(mMockTileService, times(2)).onStartListening();
     }
+
+    @Test
+    public void testNullBindingCallsUnbind() {
+        Context mockContext = mock(Context.class);
+        // Binding has to succeed
+        when(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+        TileLifecycleManager manager = new TileLifecycleManager(mHandler, mockContext,
+                mock(IQSService.class),
+                new Tile(),
+                mTileServiceIntent,
+                mUser,
+                mMockPackageManagerAdapter,
+                mMockBroadcastDispatcher);
+
+        manager.setBindService(true);
+
+        ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
+        verify(mockContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any());
+
+        captor.getValue().onNullBinding(mTileServiceComponentName);
+        verify(mockContext).unbindService(captor.getValue());
+    }
 }
