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 android.app.Service;
19 import android.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.pm.PackageManager;
25 import android.os.Handler;
26 import android.os.HandlerThread;
27 import android.os.IBinder;
28 import android.os.Process;
29 import android.os.RemoteException;
30 import android.os.UserHandle;
31 import android.service.quicksettings.IQSService;
32 import android.service.quicksettings.IQSTileService;
33 import android.service.quicksettings.Tile;
34 import android.test.AndroidTestCase;
35 import android.test.suitebuilder.annotation.SmallTest;
36 import android.util.ArraySet;
37 import android.util.Log;
38 
39 import org.mockito.Mockito;
40 
41 @SmallTest
42 public class TileLifecycleManagerTests extends AndroidTestCase {
43     public static final String TILE_UPDATE_BROADCAST = "com.android.systemui.tests.TILE_UPDATE";
44     public static final String EXTRA_CALLBACK = "callback";
45 
46     private HandlerThread mThread;
47     private Handler mHandler;
48     private TileLifecycleManager mStateManager;
49     private final Object mBroadcastLock = new Object();
50     private final ArraySet<String> mCallbacks = new ArraySet<>();
51     private boolean mBound;
52 
53     @Override
setUp()54     protected void setUp() throws Exception {
55         super.setUp();
56         mThread = new HandlerThread("TestThread");
57         mThread.start();
58         mHandler = new Handler(mThread.getLooper());
59         ComponentName component = new ComponentName(mContext, FakeTileService.class);
60         mStateManager = new TileLifecycleManager(mHandler, getContext(),
61                 Mockito.mock(IQSService.class), new Tile(component),
62                 new Intent().setComponent(component),
63                 new UserHandle(UserHandle.myUserId()));
64         mCallbacks.clear();
65         getContext().registerReceiver(mReceiver, new IntentFilter(TILE_UPDATE_BROADCAST));
66     }
67 
68     @Override
tearDown()69     protected void tearDown() throws Exception {
70         super.tearDown();
71         if (mBound) {
72             unbindService();
73         }
74         mThread.quit();
75         getContext().unregisterReceiver(mReceiver);
76     }
77 
testSync()78     public void testSync() {
79         syncWithHandler();
80     }
81 
testBind()82     public void testBind() {
83         bindService();
84         waitForCallback("onCreate");
85     }
86 
testUnbind()87     public void testUnbind() {
88         bindService();
89         waitForCallback("onCreate");
90         unbindService();
91         waitForCallback("onDestroy");
92     }
93 
testTileServiceCallbacks()94     public void testTileServiceCallbacks() {
95         bindService();
96         waitForCallback("onCreate");
97 
98         mStateManager.onTileAdded();
99         waitForCallback("onTileAdded");
100         mStateManager.onStartListening();
101         waitForCallback("onStartListening");
102         mStateManager.onClick(null);
103         waitForCallback("onClick");
104         mStateManager.onStopListening();
105         waitForCallback("onStopListening");
106         mStateManager.onTileRemoved();
107         waitForCallback("onTileRemoved");
108 
109         unbindService();
110     }
111 
testAddedBeforeBind()112     public void testAddedBeforeBind() {
113         mStateManager.onTileAdded();
114 
115         bindService();
116         waitForCallback("onCreate");
117         waitForCallback("onTileAdded");
118     }
119 
testListeningBeforeBind()120     public void testListeningBeforeBind() {
121         mStateManager.onTileAdded();
122         mStateManager.onStartListening();
123 
124         bindService();
125         waitForCallback("onCreate");
126         waitForCallback("onTileAdded");
127         waitForCallback("onStartListening");
128     }
129 
testClickBeforeBind()130     public void testClickBeforeBind() {
131         mStateManager.onTileAdded();
132         mStateManager.onStartListening();
133         mStateManager.onClick(null);
134 
135         bindService();
136         waitForCallback("onCreate");
137         waitForCallback("onTileAdded");
138         waitForCallback("onStartListening");
139         waitForCallback("onClick");
140     }
141 
testListeningNotListeningBeforeBind()142     public void testListeningNotListeningBeforeBind() {
143         mStateManager.onTileAdded();
144         mStateManager.onStartListening();
145         mStateManager.onStopListening();
146 
147         bindService();
148         waitForCallback("onCreate");
149         unbindService();
150         waitForCallback("onDestroy");
151         assertFalse(mCallbacks.contains("onStartListening"));
152     }
153 
testNoClickOfNotListeningAnymore()154     public void testNoClickOfNotListeningAnymore() {
155         mStateManager.onTileAdded();
156         mStateManager.onStartListening();
157         mStateManager.onClick(null);
158         mStateManager.onStopListening();
159 
160         bindService();
161         waitForCallback("onCreate");
162         unbindService();
163         waitForCallback("onDestroy");
164         assertFalse(mCallbacks.contains("onClick"));
165     }
166 
testComponentEnabling()167     public void testComponentEnabling() {
168         mStateManager.onTileAdded();
169         mStateManager.onStartListening();
170 
171         PackageManager pm = getContext().getPackageManager();
172         pm.setComponentEnabledSetting(new ComponentName(getContext(), FakeTileService.class),
173                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
174 
175         bindService();
176         assertTrue(mStateManager.mReceiverRegistered);
177 
178         pm.setComponentEnabledSetting(new ComponentName(getContext(), FakeTileService.class),
179                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
180         waitForCallback("onCreate");
181     }
182 
testKillProcess()183     public void testKillProcess() {
184         mStateManager.onStartListening();
185         bindService();
186         waitForCallback("onCreate");
187         waitForCallback("onStartListening");
188 
189         getContext().sendBroadcast(new Intent(FakeTileService.ACTION_KILL));
190 
191         waitForCallback("onCreate");
192         waitForCallback("onStartListening");
193     }
194 
bindService()195     private void bindService() {
196         mBound = true;
197         mStateManager.setBindService(true);
198     }
199 
unbindService()200     private void unbindService() {
201         mBound = false;
202         mStateManager.setBindService(false);
203     }
204 
waitForCallback(String callback)205     private void waitForCallback(String callback) {
206         for (int i = 0; i < 50; i++) {
207             if (mCallbacks.contains(callback)) {
208                 mCallbacks.remove(callback);
209                 return;
210             }
211             synchronized (mBroadcastLock) {
212                 try {
213                     mBroadcastLock.wait(500);
214                 } catch (InterruptedException e) {
215                 }
216             }
217         }
218         if (mCallbacks.contains(callback)) {
219             mCallbacks.remove(callback);
220             return;
221         }
222         fail("Didn't receive callback: " + callback);
223     }
224 
syncWithHandler()225     private void syncWithHandler() {
226         final Object lock = new Object();
227         synchronized (lock) {
228             mHandler.post(new Runnable() {
229                 @Override
230                 public void run() {
231                     synchronized (lock) {
232                         lock.notify();
233                     }
234                 }
235             });
236             try {
237                 lock.wait(10000);
238             } catch (InterruptedException e) {
239             }
240         }
241     }
242 
243     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
244         @Override
245         public void onReceive(Context context, Intent intent) {
246             mCallbacks.add(intent.getStringExtra(EXTRA_CALLBACK));
247             synchronized (mBroadcastLock) {
248                 mBroadcastLock.notify();
249             }
250         }
251     };
252 
253     public static class FakeTileService extends Service {
254         public static final String ACTION_KILL = "com.android.systemui.test.KILL";
255 
256         @Override
onBind(Intent intent)257         public IBinder onBind(Intent intent) {
258             return new IQSTileService.Stub() {
259                 @Override
260                 public void onTileAdded() throws RemoteException {
261                     sendCallback("onTileAdded");
262                 }
263 
264                 @Override
265                 public void onTileRemoved() throws RemoteException {
266                     sendCallback("onTileRemoved");
267                 }
268 
269                 @Override
270                 public void onStartListening() throws RemoteException {
271                     sendCallback("onStartListening");
272                 }
273 
274                 @Override
275                 public void onStopListening() throws RemoteException {
276                     sendCallback("onStopListening");
277                 }
278 
279                 @Override
280                 public void onClick(IBinder iBinder) throws RemoteException {
281                     sendCallback("onClick");
282                 }
283 
284                 @Override
285                 public void onUnlockComplete() throws RemoteException {
286                     sendCallback("onUnlockComplete");
287                 }
288             };
289         }
290 
291         @Override
onCreate()292         public void onCreate() {
293             super.onCreate();
294             registerReceiver(mReceiver, new IntentFilter(ACTION_KILL));
295             sendCallback("onCreate");
296         }
297 
298         @Override
onDestroy()299         public void onDestroy() {
300             super.onDestroy();
301             unregisterReceiver(mReceiver);
302             sendCallback("onDestroy");
303         }
304 
sendCallback(String callback)305         private void sendCallback(String callback) {
306             Log.d("TileLifecycleManager", "Relaying: " + callback);
307             sendBroadcast(new Intent(TILE_UPDATE_BROADCAST)
308                     .putExtra(EXTRA_CALLBACK, callback));
309         }
310 
311         private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
312             @Override
313             public void onReceive(Context context, Intent intent) {
314                 if (ACTION_KILL.equals(intent.getAction())) {
315                     Process.killProcess(Process.myPid());
316                 }
317             }
318         };
319     }
320 }
321