1 /* 2 * Copyright (C) 2017 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 17 package android.server.wm; 18 19 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE; 20 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26; 21 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; 22 import static android.content.Context.BIND_ALLOW_OOM_MANAGEMENT; 23 import static android.content.Context.BIND_AUTO_CREATE; 24 import static android.content.Context.BIND_NOT_FOREGROUND; 25 import static android.server.wm.alertwindowappsdk25.Components.SDK25_ALERT_WINDOW_TEST_ACTIVITY; 26 import static android.server.wm.alertwindowservice.Components.ALERT_WINDOW_SERVICE; 27 28 import static org.junit.Assert.assertEquals; 29 30 import android.app.ActivityManager; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.res.Configuration; 35 import android.content.ServiceConnection; 36 import android.os.Handler; 37 import android.os.IBinder; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.Messenger; 41 import android.os.SystemClock; 42 import android.platform.test.annotations.AppModeFull; 43 import android.platform.test.annotations.Presubmit; 44 import android.server.wm.alertwindowservice.AlertWindowService; 45 import android.support.test.InstrumentationRegistry; 46 import android.support.test.runner.AndroidJUnit4; 47 import android.util.Log; 48 49 import com.android.compatibility.common.util.SystemUtil; 50 51 import org.junit.After; 52 import org.junit.Before; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 56 import java.util.concurrent.TimeUnit; 57 import java.util.function.ToIntFunction; 58 59 /** 60 * Build/Install/Run: 61 * atest CtsWindowManagerDeviceTestCases:AlertWindowsImportanceTests 62 */ 63 @Presubmit 64 @RunWith(AndroidJUnit4.class) 65 public final class AlertWindowsImportanceTests { 66 67 private static final String TAG = "AlertWindowsTests"; 68 69 private static final boolean DEBUG = false; 70 private static final long WAIT_TIME_MS = 2 * 1000; 71 72 private Messenger mService; 73 private String mServicePackageName; 74 75 private ActivityManager mAm; 76 private ActivityManager mAm25; // ActivityManager created for an SDK 25 app context. 77 78 private final Messenger mMessenger = new Messenger(new IncomingHandler(Looper.getMainLooper())); 79 private final Object mAddedLock = new Object(); 80 private final Object mRemoveLock = new Object(); 81 82 @Before 83 public void setUp() throws Exception { 84 if (DEBUG) Log.e(TAG, "setUp"); 85 final Context context = InstrumentationRegistry.getTargetContext(); 86 87 mAm = context.getSystemService(ActivityManager.class); 88 mAm25 = context.createPackageContext(SDK25_ALERT_WINDOW_TEST_ACTIVITY.getPackageName(), 0) 89 .getSystemService(ActivityManager.class); 90 91 final Intent intent = new Intent() 92 .setComponent(ALERT_WINDOW_SERVICE) 93 .putExtra(AlertWindowService.EXTRA_MESSENGER, mMessenger); 94 // Needs to be both BIND_NOT_FOREGROUND and BIND_ALLOW_OOM_MANAGEMENT to avoid the binding 95 // to this instrumentation test from increasing its importance. 96 context.bindService(intent, mConnection, 97 BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_ALLOW_OOM_MANAGEMENT); 98 synchronized (mConnection) { 99 // Wait for alert window service to be connection before processing. 100 mConnection.wait(WAIT_TIME_MS); 101 } 102 } 103 104 @After 105 public void tearDown() throws Exception { 106 if (DEBUG) Log.e(TAG, "tearDown"); 107 if (mService != null) { 108 mService.send(Message.obtain(null, AlertWindowService.MSG_REMOVE_ALL_ALERT_WINDOWS)); 109 } 110 final Context context = InstrumentationRegistry.getTargetContext(); 111 context.unbindService(mConnection); 112 mAm = null; 113 mAm25 = null; 114 } 115 116 @Test 117 @AppModeFull(reason = "Uses apps targeting older SDK") 118 public void testAlertWindowOomAdj() throws Exception { 119 // Alert windows are always hidden when running in VR. 120 if (isRunningInVR()) { 121 return; 122 } 123 setAlertWindowPermission(true /* allow */); 124 125 assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26); 126 127 // TODO AM.getUidImportance() sometimes return a different value from what 128 // getPackageImportance() returns... b/37950472 129 // assertUidImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26); 130 131 addAlertWindow(); 132 // Process importance should be increased to visible when the service has an alert window. 133 assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE); 134 135 addAlertWindow(); 136 assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE); 137 138 setAlertWindowPermission(false /* allow */); 139 // Process importance should no longer be visible since its alert windows are not allowed to 140 // be visible. 141 assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26); 142 143 setAlertWindowPermission(true /* allow */); 144 // They can show again so importance should be visible again. 145 assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE); 146 147 removeAlertWindow(); 148 assertPackageImportance(IMPORTANCE_VISIBLE, IMPORTANCE_VISIBLE); 149 150 removeAlertWindow(); 151 // Process importance should no longer be visible when the service no longer as alert 152 // windows. 153 assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26); 154 } 155 156 private void addAlertWindow() throws Exception { 157 mService.send(Message.obtain(null, AlertWindowService.MSG_ADD_ALERT_WINDOW)); 158 synchronized (mAddedLock) { 159 // Wait for window addition confirmation before proceeding. 160 mAddedLock.wait(WAIT_TIME_MS); 161 } 162 } 163 164 private void removeAlertWindow() throws Exception { 165 mService.send(Message.obtain(null, AlertWindowService.MSG_REMOVE_ALERT_WINDOW)); 166 synchronized (mRemoveLock) { 167 // Wait for window removal confirmation before proceeding. 168 mRemoveLock.wait(WAIT_TIME_MS); 169 } 170 } 171 172 private void setAlertWindowPermission(boolean allow) throws Exception { 173 final String cmd = "appops set " + mServicePackageName 174 + " android:system_alert_window " + (allow ? "allow" : "deny"); 175 SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd); 176 } 177 178 private void assertImportance(ToIntFunction<ActivityManager> apiCaller, 179 int expectedForO, int expectedForPreO) throws Exception { 180 final long TIMEOUT = SystemClock.uptimeMillis() + TimeUnit.SECONDS.toMillis(30); 181 int actual; 182 183 do { 184 // TODO: We should try to use ActivityManagerTest.UidImportanceListener here to listen 185 // for changes in the uid importance. However, the way it is currently structured 186 // doesn't really work for this use case right now... 187 Thread.sleep(500); 188 actual = apiCaller.applyAsInt(mAm); 189 } while (actual != expectedForO && (SystemClock.uptimeMillis() < TIMEOUT)); 190 191 assertEquals(expectedForO, actual); 192 193 // Check the result for pre-O apps. 194 assertEquals(expectedForPreO, apiCaller.applyAsInt(mAm25)); 195 } 196 197 /** 198 * Make sure {@link ActivityManager#getPackageImportance} returns the expected value. 199 */ 200 private void assertPackageImportance(int expectedForO, int expectedForPreO) throws Exception { 201 assertImportance(am -> am.getPackageImportance(mServicePackageName), 202 expectedForO, expectedForPreO); 203 } 204 205 private final ServiceConnection mConnection = new ServiceConnection() { 206 @Override 207 public void onServiceConnected(ComponentName name, IBinder service) { 208 if (DEBUG) Log.e(TAG, "onServiceConnected"); 209 mService = new Messenger(service); 210 mServicePackageName = name.getPackageName(); 211 synchronized (mConnection) { 212 notifyAll(); 213 } 214 } 215 216 @Override 217 public void onServiceDisconnected(ComponentName name) { 218 if (DEBUG) Log.e(TAG, "onServiceDisconnected"); 219 mService = null; 220 mServicePackageName = null; 221 } 222 }; 223 224 private class IncomingHandler extends Handler { 225 226 IncomingHandler(Looper looper) { 227 super(looper); 228 } 229 230 @Override 231 public void handleMessage(Message msg) { 232 switch (msg.what) { 233 case AlertWindowService.MSG_ON_ALERT_WINDOW_ADDED: 234 synchronized (mAddedLock) { 235 if (DEBUG) Log.e(TAG, "MSG_ON_ALERT_WINDOW_ADDED"); 236 mAddedLock.notifyAll(); 237 } 238 break; 239 case AlertWindowService.MSG_ON_ALERT_WINDOW_REMOVED: 240 synchronized (mRemoveLock) { 241 if (DEBUG) Log.e(TAG, "MSG_ON_ALERT_WINDOW_REMOVED"); 242 mRemoveLock.notifyAll(); 243 } 244 break; 245 default: 246 super.handleMessage(msg); 247 } 248 } 249 } 250 251 private boolean isRunningInVR() { 252 final Context context = InstrumentationRegistry.getTargetContext(); 253 if ((context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) 254 == Configuration.UI_MODE_TYPE_VR_HEADSET) { 255 return true; 256 } 257 return false; 258 } 259 } 260