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