1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.systemui.qs.external;
17 
18 import static junit.framework.Assert.assertFalse;
19 import static junit.framework.Assert.assertTrue;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.mockito.Mockito.any;
23 import static org.mockito.Mockito.anyInt;
24 import static org.mockito.Mockito.anyString;
25 import static org.mockito.Mockito.never;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
29 
30 import android.content.ComponentName;
31 import android.content.Intent;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.ServiceInfo;
34 import android.net.Uri;
35 import android.os.Bundle;
36 import android.os.Handler;
37 import android.os.HandlerThread;
38 import android.os.UserHandle;
39 import android.service.quicksettings.IQSService;
40 import android.service.quicksettings.IQSTileService;
41 import android.service.quicksettings.Tile;
42 import android.service.quicksettings.TileService;
43 import android.test.suitebuilder.annotation.SmallTest;
44 
45 import androidx.test.runner.AndroidJUnit4;
46 
47 import com.android.systemui.SysuiTestCase;
48 import com.android.systemui.broadcast.BroadcastDispatcher;
49 
50 import org.junit.After;
51 import org.junit.Before;
52 import org.junit.Test;
53 import org.junit.runner.RunWith;
54 import org.mockito.Mockito;
55 
56 @SmallTest
57 @RunWith(AndroidJUnit4.class)
58 public class TileLifecycleManagerTest extends SysuiTestCase {
59     private static final int TEST_FAIL_TIMEOUT = 5000;
60 
61     private final PackageManagerAdapter mMockPackageManagerAdapter =
62             Mockito.mock(PackageManagerAdapter.class);
63     private final BroadcastDispatcher mMockBroadcastDispatcher =
64             Mockito.mock(BroadcastDispatcher.class);
65     private final IQSTileService.Stub mMockTileService = Mockito.mock(IQSTileService.Stub.class);
66     private ComponentName mTileServiceComponentName;
67     private Intent mTileServiceIntent;
68     private UserHandle mUser;
69     private HandlerThread mThread;
70     private Handler mHandler;
71     private TileLifecycleManager mStateManager;
72 
73     @Before
setUp()74     public void setUp() throws Exception {
75         setPackageEnabled(true);
76         mTileServiceComponentName = new ComponentName(mContext, "FakeTileService.class");
77 
78         // Stub.asInterface will just return itself.
79         when(mMockTileService.queryLocalInterface(anyString())).thenReturn(mMockTileService);
80         when(mMockTileService.asBinder()).thenReturn(mMockTileService);
81 
82         mContext.addMockService(mTileServiceComponentName, mMockTileService);
83 
84         mTileServiceIntent = new Intent().setComponent(mTileServiceComponentName);
85         mUser = new UserHandle(UserHandle.myUserId());
86         mThread = new HandlerThread("TestThread");
87         mThread.start();
88         mHandler = Handler.createAsync(mThread.getLooper());
89         mStateManager = new TileLifecycleManager(mHandler, mContext,
90                 Mockito.mock(IQSService.class), new Tile(),
91                 mTileServiceIntent,
92                 mUser,
93                 mMockPackageManagerAdapter,
94                 mMockBroadcastDispatcher);
95     }
96 
97     @After
tearDown()98     public void tearDown() throws Exception {
99         mThread.quit();
100     }
101 
setPackageEnabled(boolean enabled)102     private void setPackageEnabled(boolean enabled) throws Exception {
103         ServiceInfo defaultServiceInfo = null;
104         if (enabled) {
105             defaultServiceInfo = new ServiceInfo();
106             defaultServiceInfo.metaData = new Bundle();
107             defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, true);
108             defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_TOGGLEABLE_TILE, true);
109         }
110         when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), anyInt()))
111                 .thenReturn(defaultServiceInfo);
112         when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt()))
113                 .thenReturn(defaultServiceInfo);
114         PackageInfo defaultPackageInfo = new PackageInfo();
115         when(mMockPackageManagerAdapter.getPackageInfoAsUser(anyString(), anyInt(), anyInt()))
116                 .thenReturn(defaultPackageInfo);
117     }
118 
verifyBind(int times)119     private void verifyBind(int times) {
120         assertEquals(times > 0, mContext.isBound(mTileServiceComponentName));
121     }
122 
123     @Test
testBind()124     public void testBind() {
125         mStateManager.setBindService(true);
126         verifyBind(1);
127     }
128 
129     @Test
testUnbind()130     public void testUnbind() {
131         mStateManager.setBindService(true);
132         mStateManager.setBindService(false);
133         assertFalse(mContext.isBound(mTileServiceComponentName));
134     }
135 
136     @Test
testTileServiceCallbacks()137     public void testTileServiceCallbacks() throws Exception {
138         mStateManager.setBindService(true);
139         mStateManager.onTileAdded();
140         verify(mMockTileService).onTileAdded();
141         mStateManager.onStartListening();
142         verify(mMockTileService).onStartListening();
143         mStateManager.onClick(null);
144         verify(mMockTileService).onClick(null);
145         mStateManager.onStopListening();
146         verify(mMockTileService).onStopListening();
147         mStateManager.onTileRemoved();
148         verify(mMockTileService).onTileRemoved();
149     }
150 
151     @Test
testAddedBeforeBind()152     public void testAddedBeforeBind() throws Exception {
153         mStateManager.onTileAdded();
154         mStateManager.setBindService(true);
155 
156         verifyBind(1);
157         verify(mMockTileService).onTileAdded();
158     }
159 
160     @Test
testListeningBeforeBind()161     public void testListeningBeforeBind() throws Exception {
162         mStateManager.onTileAdded();
163         mStateManager.onStartListening();
164         mStateManager.setBindService(true);
165 
166         verifyBind(1);
167         verify(mMockTileService).onTileAdded();
168         verify(mMockTileService).onStartListening();
169     }
170 
171     @Test
testClickBeforeBind()172     public void testClickBeforeBind() throws Exception {
173         mStateManager.onTileAdded();
174         mStateManager.onStartListening();
175         mStateManager.onClick(null);
176         mStateManager.setBindService(true);
177 
178         verifyBind(1);
179         verify(mMockTileService).onTileAdded();
180         verify(mMockTileService).onStartListening();
181         verify(mMockTileService).onClick(null);
182     }
183 
184     @Test
testListeningNotListeningBeforeBind()185     public void testListeningNotListeningBeforeBind() throws Exception {
186         mStateManager.onTileAdded();
187         mStateManager.onStartListening();
188         mStateManager.onStopListening();
189         mStateManager.setBindService(true);
190 
191         verifyBind(1);
192         mStateManager.setBindService(false);
193         assertFalse(mContext.isBound(mTileServiceComponentName));
194         verify(mMockTileService, never()).onStartListening();
195     }
196 
197     @Test
testNoClickOfNotListeningAnymore()198     public void testNoClickOfNotListeningAnymore() throws Exception {
199         mStateManager.onTileAdded();
200         mStateManager.onStartListening();
201         mStateManager.onClick(null);
202         mStateManager.onStopListening();
203         mStateManager.setBindService(true);
204 
205         verifyBind(1);
206         mStateManager.setBindService(false);
207         assertFalse(mContext.isBound(mTileServiceComponentName));
208         verify(mMockTileService, never()).onClick(null);
209     }
210 
211     @Test
testComponentEnabling()212     public void testComponentEnabling() throws Exception {
213         mStateManager.onTileAdded();
214         mStateManager.onStartListening();
215         setPackageEnabled(false);
216         mStateManager.setBindService(true);
217         // Package not available, not yet created.
218         verifyBind(0);
219 
220         // Package is re-enabled.
221         setPackageEnabled(true);
222         mStateManager.onReceive(
223                 mContext,
224                 new Intent(
225                         Intent.ACTION_PACKAGE_CHANGED,
226                         Uri.fromParts(
227                                 "package", mTileServiceComponentName.getPackageName(), null)));
228         verifyBind(1);
229     }
230 
231     @Test
testKillProcess()232     public void testKillProcess() throws Exception {
233         mStateManager.onStartListening();
234         mStateManager.setBindService(true);
235         mStateManager.setBindRetryDelay(0);
236         mStateManager.onServiceDisconnected(mTileServiceComponentName);
237 
238         // Guarantees mHandler has processed all messages.
239         assertTrue(mHandler.runWithScissors(()->{}, TEST_FAIL_TIMEOUT));
240 
241         // Two calls: one for the first bind, one for the restart.
242         verifyBind(2);
243         verify(mMockTileService, times(2)).onStartListening();
244     }
245 
246     @Test
testToggleableTile()247     public void testToggleableTile() throws Exception {
248         assertTrue(mStateManager.isToggleableTile());
249     }
250 }
251