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 android.os.PowerExemptionManager.REASON_TILE_ONCLICK;
19 import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
20 import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT;
21 
22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
24 import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
25 
26 import static junit.framework.Assert.assertFalse;
27 import static junit.framework.Assert.assertTrue;
28 
29 import static org.junit.Assert.assertEquals;
30 import static org.junit.Assert.assertNotEquals;
31 import static org.mockito.ArgumentMatchers.eq;
32 import static org.mockito.Mockito.any;
33 import static org.mockito.Mockito.anyInt;
34 import static org.mockito.Mockito.anyString;
35 import static org.mockito.Mockito.clearInvocations;
36 import static org.mockito.Mockito.doAnswer;
37 import static org.mockito.Mockito.mock;
38 import static org.mockito.Mockito.never;
39 import static org.mockito.Mockito.times;
40 import static org.mockito.Mockito.verify;
41 import static org.mockito.Mockito.when;
42 
43 import android.app.ActivityManager;
44 import android.app.compat.CompatChanges;
45 import android.content.BroadcastReceiver;
46 import android.content.ComponentName;
47 import android.content.Context;
48 import android.content.ContextWrapper;
49 import android.content.Intent;
50 import android.content.IntentFilter;
51 import android.content.ServiceConnection;
52 import android.content.pm.PackageInfo;
53 import android.content.pm.ServiceInfo;
54 import android.net.Uri;
55 import android.os.Bundle;
56 import android.os.Handler;
57 import android.os.HandlerThread;
58 import android.os.IDeviceIdleController;
59 import android.os.UserHandle;
60 import android.platform.test.annotations.DisableFlags;
61 import android.platform.test.annotations.EnableFlags;
62 import android.platform.test.flag.junit.FlagsParameterization;
63 import android.service.quicksettings.IQSService;
64 import android.service.quicksettings.IQSTileService;
65 import android.service.quicksettings.TileService;
66 
67 import androidx.annotation.Nullable;
68 import androidx.test.filters.SmallTest;
69 
70 import com.android.systemui.SysuiTestCase;
71 import com.android.systemui.broadcast.BroadcastDispatcher;
72 import com.android.systemui.util.concurrency.FakeExecutor;
73 import com.android.systemui.util.time.FakeSystemClock;
74 
75 import org.junit.After;
76 import org.junit.Before;
77 import org.junit.Test;
78 import org.junit.runner.RunWith;
79 import org.mockito.ArgumentCaptor;
80 import org.mockito.InOrder;
81 import org.mockito.Mockito;
82 import org.mockito.MockitoSession;
83 
84 import java.util.List;
85 
86 import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
87 import platform.test.runner.parameterized.Parameters;
88 
89 @SmallTest
90 @RunWith(ParameterizedAndroidJunit4.class)
91 public class TileLifecycleManagerTest extends SysuiTestCase {
92 
93     @Parameters(name = "{0}")
getParams()94     public static List<FlagsParameterization> getParams() {
95         return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
96     }
97 
98     private final PackageManagerAdapter mMockPackageManagerAdapter =
99             mock(PackageManagerAdapter.class);
100     private final BroadcastDispatcher mMockBroadcastDispatcher =
101             mock(BroadcastDispatcher.class);
102     private final IQSTileService.Stub mMockTileService = mock(IQSTileService.Stub.class);
103     private final ActivityManager mActivityManager = mock(ActivityManager.class);
104     private final IDeviceIdleController mDeviceIdleController = mock(IDeviceIdleController.class);
105 
106     private ComponentName mTileServiceComponentName;
107     private Intent mTileServiceIntent;
108     private UserHandle mUser;
109     private FakeSystemClock mClock;
110     private FakeExecutor mExecutor;
111     private HandlerThread mThread;
112     private Handler mHandler;
113     private TileLifecycleManager mStateManager;
114     private TestContextWrapper mWrappedContext;
115     private MockitoSession mMockitoSession;
116 
TileLifecycleManagerTest(FlagsParameterization flags)117     public TileLifecycleManagerTest(FlagsParameterization flags) {
118         super();
119         mSetFlagsRule.setFlagsParameterization(flags);
120     }
121 
122     @Before
setUp()123     public void setUp() throws Exception {
124         setPackageEnabled(true);
125         mTileServiceComponentName = new ComponentName(mContext, "FakeTileService.class");
126         mMockitoSession = mockitoSession()
127                 .initMocks(this)
128                 .mockStatic(CompatChanges.class)
129                 .startMocking();
130 
131         // Stub.asInterface will just return itself.
132         when(mMockTileService.queryLocalInterface(anyString())).thenReturn(mMockTileService);
133         when(mMockTileService.asBinder()).thenReturn(mMockTileService);
134 
135         mContext.addMockService(mTileServiceComponentName, mMockTileService);
136 
137         mWrappedContext = new TestContextWrapper(mContext);
138 
139         mTileServiceIntent = new Intent().setComponent(mTileServiceComponentName);
140         mUser = new UserHandle(UserHandle.myUserId());
141         mThread = new HandlerThread("TestThread");
142         mThread.start();
143         mHandler = Handler.createAsync(mThread.getLooper());
144         mClock = new FakeSystemClock();
145         mExecutor = new FakeExecutor(mClock);
146         mStateManager = new TileLifecycleManager(mHandler, mWrappedContext,
147                 mock(IQSService.class),
148                 mMockPackageManagerAdapter,
149                 mMockBroadcastDispatcher,
150                 mTileServiceIntent,
151                 mUser,
152                 mActivityManager,
153                 mDeviceIdleController,
154                 mExecutor);
155     }
156 
157     @After
tearDown()158     public void tearDown() throws Exception {
159         if (mMockitoSession != null) {
160             mMockitoSession.finishMocking();
161         }
162         if (mThread != null) {
163             mThread.quit();
164         }
165 
166         mStateManager.handleDestroy();
167     }
168 
setPackageEnabled(boolean enabled)169     private void setPackageEnabled(boolean enabled) throws Exception {
170         ServiceInfo defaultServiceInfo = null;
171         if (enabled) {
172             defaultServiceInfo = new ServiceInfo();
173             defaultServiceInfo.metaData = new Bundle();
174             defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, true);
175             defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_TOGGLEABLE_TILE, true);
176         }
177         when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), anyInt()))
178                 .thenReturn(defaultServiceInfo);
179         when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt()))
180                 .thenReturn(defaultServiceInfo);
181         PackageInfo defaultPackageInfo = new PackageInfo();
182         when(mMockPackageManagerAdapter.getPackageInfoAsUser(anyString(), anyInt(), anyInt()))
183                 .thenReturn(defaultPackageInfo);
184     }
185 
verifyBind(int times)186     private void verifyBind(int times) {
187         assertEquals(times > 0, mContext.isBound(mTileServiceComponentName));
188     }
189 
190     @Test
testBind()191     public void testBind() {
192         mStateManager.executeSetBindService(true);
193         mExecutor.runAllReady();
194         verifyBind(1);
195     }
196 
197     @Test
testPackageReceiverExported()198     public void testPackageReceiverExported() throws Exception {
199         // Make sure that we register a receiver
200         setPackageEnabled(false);
201         mStateManager.executeSetBindService(true);
202         mExecutor.runAllReady();
203         IntentFilter filter = mWrappedContext.mLastIntentFilter;
204         assertTrue(filter.hasAction(Intent.ACTION_PACKAGE_ADDED));
205         assertTrue(filter.hasAction(Intent.ACTION_PACKAGE_CHANGED));
206         assertTrue(filter.hasDataScheme("package"));
207         assertNotEquals(0, mWrappedContext.mLastFlag & Context.RECEIVER_EXPORTED);
208     }
209 
210     @Test
testUnbind()211     public void testUnbind() {
212         mStateManager.executeSetBindService(true);
213         mExecutor.runAllReady();
214         mStateManager.executeSetBindService(false);
215         mExecutor.runAllReady();
216         assertFalse(mContext.isBound(mTileServiceComponentName));
217     }
218 
219     @Test
testTileServiceCallbacks()220     public void testTileServiceCallbacks() throws Exception {
221         mStateManager.executeSetBindService(true);
222         mExecutor.runAllReady();
223         mStateManager.onTileAdded();
224         verify(mMockTileService).onTileAdded();
225         mStateManager.onStartListening();
226         verify(mMockTileService).onStartListening();
227         mStateManager.onClick(null);
228         verify(mMockTileService).onClick(null);
229         mStateManager.onStopListening();
230         verify(mMockTileService).onStopListening();
231         mStateManager.onTileRemoved();
232         verify(mMockTileService).onTileRemoved();
233     }
234 
235     @Test
testAddedBeforeBind()236     public void testAddedBeforeBind() throws Exception {
237         mStateManager.onTileAdded();
238         mStateManager.executeSetBindService(true);
239         mExecutor.runAllReady();
240 
241         verifyBind(1);
242         verify(mMockTileService).onTileAdded();
243     }
244 
245     @Test
testListeningBeforeBind()246     public void testListeningBeforeBind() throws Exception {
247         mStateManager.onTileAdded();
248         mStateManager.onStartListening();
249         mStateManager.executeSetBindService(true);
250         mExecutor.runAllReady();
251 
252         verifyBind(1);
253         verify(mMockTileService).onTileAdded();
254         verify(mMockTileService).onStartListening();
255     }
256 
257     @Test
testClickBeforeBind()258     public void testClickBeforeBind() throws Exception {
259         mStateManager.onTileAdded();
260         mStateManager.onStartListening();
261         mStateManager.onClick(null);
262         mStateManager.executeSetBindService(true);
263         mExecutor.runAllReady();
264 
265         verifyBind(1);
266         verify(mMockTileService).onTileAdded();
267         verify(mMockTileService).onStartListening();
268         verify(mMockTileService).onClick(null);
269     }
270 
271     @Test
testListeningNotListeningBeforeBind()272     public void testListeningNotListeningBeforeBind() throws Exception {
273         mStateManager.onTileAdded();
274         mStateManager.onStartListening();
275         mStateManager.onStopListening();
276         mStateManager.executeSetBindService(true);
277         mExecutor.runAllReady();
278 
279         verifyBind(1);
280         mStateManager.executeSetBindService(false);
281         mExecutor.runAllReady();
282         assertFalse(mContext.isBound(mTileServiceComponentName));
283         verify(mMockTileService, never()).onStartListening();
284     }
285 
286     @Test
287     @DisableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
testNoClickIfNotListeningAnymore()288     public void testNoClickIfNotListeningAnymore() throws Exception {
289         mStateManager.onTileAdded();
290         mStateManager.onStartListening();
291         mStateManager.onClick(null);
292         mStateManager.onStopListening();
293         mStateManager.executeSetBindService(true);
294         mExecutor.runAllReady();
295 
296         verifyBind(1);
297         mStateManager.executeSetBindService(false);
298         mExecutor.runAllReady();
299         assertFalse(mContext.isBound(mTileServiceComponentName));
300         verify(mMockTileService, never()).onClick(null);
301     }
302 
303     @Test
304     @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
testNoClickIfNotListeningBeforeClick()305     public void testNoClickIfNotListeningBeforeClick() throws Exception {
306         mStateManager.onTileAdded();
307         mStateManager.onStartListening();
308         mStateManager.onStopListening();
309         mStateManager.onClick(null);
310         mStateManager.executeSetBindService(true);
311         mExecutor.runAllReady();
312 
313         verifyBind(1);
314         mStateManager.executeSetBindService(false);
315         mExecutor.runAllReady();
316         assertFalse(mContext.isBound(mTileServiceComponentName));
317         verify(mMockTileService, never()).onClick(null);
318     }
319 
320     @Test
321     @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX)
testClickIfStopListeningBeforeProcessedClick()322     public void testClickIfStopListeningBeforeProcessedClick() throws Exception {
323         mStateManager.onTileAdded();
324         mStateManager.onStartListening();
325         mStateManager.onClick(null);
326         mStateManager.onStopListening();
327         mStateManager.executeSetBindService(true);
328         mExecutor.runAllReady();
329 
330         verifyBind(1);
331         mStateManager.executeSetBindService(false);
332         mExecutor.runAllReady();
333         assertFalse(mContext.isBound(mTileServiceComponentName));
334         InOrder inOrder = Mockito.inOrder(mMockTileService);
335         inOrder.verify(mMockTileService).onClick(null);
336         inOrder.verify(mMockTileService).onStopListening();
337     }
338 
339     @Test
testComponentEnabling()340     public void testComponentEnabling() throws Exception {
341         mStateManager.onTileAdded();
342         mStateManager.onStartListening();
343         setPackageEnabled(false);
344         mStateManager.executeSetBindService(true);
345         mExecutor.runAllReady();
346         // Package not available, not yet created.
347         verifyBind(0);
348 
349         // Package is re-enabled.
350         setPackageEnabled(true);
351         mStateManager.onReceive(
352                 mContext,
353                 new Intent(
354                         Intent.ACTION_PACKAGE_CHANGED,
355                         Uri.fromParts(
356                                 "package", mTileServiceComponentName.getPackageName(), null)));
357         mExecutor.runAllReady();
358         verifyBind(1);
359     }
360 
361     @Test
testKillProcess()362     public void testKillProcess() throws Exception {
363         mStateManager.onStartListening();
364         mStateManager.executeSetBindService(true);
365         mExecutor.runAllReady();
366         mStateManager.onBindingDied(mTileServiceComponentName);
367         mExecutor.runAllReady();
368         mClock.advanceTime(5000);
369         mExecutor.runAllReady();
370 
371         // Two calls: one for the first bind, one for the restart.
372         verifyBind(2);
373         verify(mMockTileService, times(2)).onStartListening();
374     }
375 
376     @Test
testKillProcessLowMemory()377     public void testKillProcessLowMemory() throws Exception {
378         doAnswer(invocation -> {
379             ActivityManager.MemoryInfo memoryInfo = invocation.getArgument(0);
380             memoryInfo.lowMemory = true;
381             return null;
382         }).when(mActivityManager).getMemoryInfo(any());
383         mStateManager.onStartListening();
384         mStateManager.executeSetBindService(true);
385         mExecutor.runAllReady();
386         verify(mMockTileService, times(1)).onStartListening();
387         mStateManager.onBindingDied(mTileServiceComponentName);
388         mExecutor.runAllReady();
389 
390         // Longer delay than a regular one
391         mClock.advanceTime(5000);
392         mExecutor.runAllReady();
393 
394         assertFalse(mContext.isBound(mTileServiceComponentName));
395 
396         mClock.advanceTime(20000);
397         mExecutor.runAllReady();
398         // Two calls: one for the first bind, one for the restart.
399         verifyBind(2);
400         verify(mMockTileService, times(2)).onStartListening();
401     }
402 
403     @Test
testOnServiceDisconnectedDoesnUnbind_doesntForwardToBinder()404     public void testOnServiceDisconnectedDoesnUnbind_doesntForwardToBinder() throws Exception {
405         mStateManager.executeSetBindService(true);
406         mExecutor.runAllReady();
407 
408         mStateManager.onStartListening();
409         verify(mMockTileService).onStartListening();
410 
411         clearInvocations(mMockTileService);
412         mStateManager.onServiceDisconnected(mTileServiceComponentName);
413         mExecutor.runAllReady();
414 
415         mStateManager.onStartListening();
416         verify(mMockTileService, never()).onStartListening();
417     }
418 
419     @Test
testKillProcessLowMemory_unbound_doesntBindAgain()420     public void testKillProcessLowMemory_unbound_doesntBindAgain() throws Exception {
421         doAnswer(invocation -> {
422             ActivityManager.MemoryInfo memoryInfo = invocation.getArgument(0);
423             memoryInfo.lowMemory = true;
424             return null;
425         }).when(mActivityManager).getMemoryInfo(any());
426         mStateManager.onStartListening();
427         mStateManager.executeSetBindService(true);
428         mExecutor.runAllReady();
429         verifyBind(1);
430         verify(mMockTileService, times(1)).onStartListening();
431 
432         mStateManager.onBindingDied(mTileServiceComponentName);
433         mExecutor.runAllReady();
434 
435         clearInvocations(mMockTileService);
436         mStateManager.executeSetBindService(false);
437         mExecutor.runAllReady();
438         mClock.advanceTime(30000);
439         mExecutor.runAllReady();
440 
441         verifyBind(0);
442         verify(mMockTileService, never()).onStartListening();
443     }
444 
445     @Test
testToggleableTile()446     public void testToggleableTile() throws Exception {
447         assertTrue(mStateManager.isToggleableTile());
448     }
449 
450     @Test
testClickCallsDeviceIdleManager()451     public void testClickCallsDeviceIdleManager() throws Exception {
452         mStateManager.onTileAdded();
453         mStateManager.onStartListening();
454         mStateManager.onClick(null);
455         mStateManager.executeSetBindService(true);
456         mExecutor.runAllReady();
457 
458         verify(mMockTileService).onClick(null);
459         verify(mDeviceIdleController).addPowerSaveTempWhitelistApp(
460                 mTileServiceComponentName.getPackageName(), 15000,
461                 mUser.getIdentifier(), REASON_TILE_ONCLICK, "tile onclick");
462     }
463 
464     @Test
testFalseBindCallsUnbind()465     public void testFalseBindCallsUnbind() {
466         Context falseContext = mock(Context.class);
467         when(falseContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(false);
468         TileLifecycleManager manager = new TileLifecycleManager(mHandler, falseContext,
469                 mock(IQSService.class),
470                 mMockPackageManagerAdapter,
471                 mMockBroadcastDispatcher,
472                 mTileServiceIntent,
473                 mUser,
474                 mActivityManager,
475                 mDeviceIdleController,
476                 mExecutor);
477 
478         manager.executeSetBindService(true);
479         mExecutor.runAllReady();
480 
481         ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
482         verify(falseContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any());
483 
484         verify(falseContext).unbindService(captor.getValue());
485     }
486 
487     @Test
testVersionUDoesNotBindsAllowBackgroundActivity()488     public void testVersionUDoesNotBindsAllowBackgroundActivity() {
489         mockChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT, true);
490         Context falseContext = mock(Context.class);
491         TileLifecycleManager manager = new TileLifecycleManager(mHandler, falseContext,
492                 mock(IQSService.class),
493                 mMockPackageManagerAdapter,
494                 mMockBroadcastDispatcher,
495                 mTileServiceIntent,
496                 mUser,
497                 mActivityManager,
498                 mDeviceIdleController,
499                 mExecutor);
500 
501         manager.executeSetBindService(true);
502         mExecutor.runAllReady();
503         int flags = Context.BIND_AUTO_CREATE
504                 | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
505                 | Context.BIND_WAIVE_PRIORITY;
506 
507         verify(falseContext).bindServiceAsUser(any(), any(), eq(flags), any());
508     }
509 
510     @Test
testVersionLessThanUBindsAllowBackgroundActivity()511     public void testVersionLessThanUBindsAllowBackgroundActivity() {
512         mockChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT, false);
513         Context falseContext = mock(Context.class);
514         TileLifecycleManager manager = new TileLifecycleManager(mHandler, falseContext,
515                 mock(IQSService.class),
516                 mMockPackageManagerAdapter,
517                 mMockBroadcastDispatcher,
518                 mTileServiceIntent,
519                 mUser,
520                 mActivityManager,
521                 mDeviceIdleController,
522                 mExecutor);
523 
524         manager.executeSetBindService(true);
525         mExecutor.runAllReady();
526         int flags = Context.BIND_AUTO_CREATE
527                 | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
528                 | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
529                 | Context.BIND_WAIVE_PRIORITY;
530 
531         verify(falseContext).bindServiceAsUser(any(), any(), eq(flags), any());
532     }
533 
534     @Test
testNullBindingCallsUnbind()535     public void testNullBindingCallsUnbind() {
536         Context mockContext = mock(Context.class);
537         // Binding has to succeed
538         when(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
539         TileLifecycleManager manager = new TileLifecycleManager(mHandler, mockContext,
540                 mock(IQSService.class),
541                 mMockPackageManagerAdapter,
542                 mMockBroadcastDispatcher,
543                 mTileServiceIntent,
544                 mUser,
545                 mActivityManager,
546                 mDeviceIdleController,
547                 mExecutor);
548 
549         manager.executeSetBindService(true);
550         mExecutor.runAllReady();
551 
552         ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
553         verify(mockContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any());
554 
555         captor.getValue().onNullBinding(mTileServiceComponentName);
556         mExecutor.runAllReady();
557         verify(mockContext).unbindService(captor.getValue());
558     }
559 
mockChangeEnabled(long changeId, boolean enabled)560     private void mockChangeEnabled(long changeId, boolean enabled) {
561         doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(),
562                 any(UserHandle.class)));
563     }
564 
565     private static class TestContextWrapper extends ContextWrapper {
566         private IntentFilter mLastIntentFilter;
567         private int mLastFlag;
568 
TestContextWrapper(Context base)569         TestContextWrapper(Context base) {
570             super(base);
571         }
572 
573         @Override
registerReceiverAsUser(@ullable BroadcastReceiver receiver, UserHandle user, IntentFilter filter, @Nullable String broadcastPermission, @Nullable Handler scheduler, int flags)574         public Intent registerReceiverAsUser(@Nullable BroadcastReceiver receiver, UserHandle user,
575                 IntentFilter filter, @Nullable String broadcastPermission,
576                 @Nullable Handler scheduler, int flags) {
577             mLastIntentFilter = filter;
578             mLastFlag = flags;
579             return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
580                     scheduler, flags);
581         }
582     }
583 }
584