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