• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.platform.systemui.tests.jank;
18 
19 import android.app.Notification.Builder;
20 import android.app.NotificationManager;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.graphics.Rect;
26 import android.os.Bundle;
27 import android.os.Environment;
28 import android.os.RemoteException;
29 import android.os.SystemClock;
30 import android.support.test.jank.GfxMonitor;
31 import android.support.test.jank.JankTest;
32 import android.support.test.jank.JankTestBase;
33 import android.support.test.timeresulthelper.TimeResultLogger;
34 import android.support.test.uiautomator.By;
35 import android.support.test.uiautomator.BySelector;
36 import android.support.test.uiautomator.UiDevice;
37 import android.support.test.uiautomator.UiObject;
38 import android.support.test.uiautomator.UiObject2;
39 import android.support.test.uiautomator.UiSelector;
40 import android.support.test.uiautomator.Until;
41 import android.util.Log;
42 import android.widget.Button;
43 
44 import java.io.File;
45 import java.io.IOException;
46 import java.util.ArrayList;
47 import java.util.List;
48 
49 public class SystemUiJankTests extends JankTestBase {
50 
51     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
52     private static final BySelector RECENTS = By.res(SYSTEMUI_PACKAGE, "recents_view");
53     private static final String LOG_TAG = SystemUiJankTests.class.getSimpleName();
54     private static final int SWIPE_MARGIN = 5;
55     private static final int DEFAULT_FLING_STEPS = 5;
56     private static final int DEFAULT_SCROLL_STEPS = 15;
57     // short transitions should be repeated within the test function, otherwise frame stats
58     // captured are not really meaningful in a statistical sense
59     private static final int INNER_LOOP = 3;
60     private static final int[] ICONS = new int[] {
61             android.R.drawable.stat_notify_call_mute,
62             android.R.drawable.stat_notify_chat,
63             android.R.drawable.stat_notify_error,
64             android.R.drawable.stat_notify_missed_call,
65             android.R.drawable.stat_notify_more,
66             android.R.drawable.stat_notify_sdcard,
67             android.R.drawable.stat_notify_sdcard_prepare,
68             android.R.drawable.stat_notify_sdcard_usb,
69             android.R.drawable.stat_notify_sync,
70             android.R.drawable.stat_notify_sync_noanim,
71             android.R.drawable.stat_notify_voicemail,
72     };
73     private static final String NOTIFICATION_TEXT = "Lorem ipsum dolor sit amet";
74     private static final File TIMESTAMP_FILE = new File(Environment.getExternalStorageDirectory()
75             .getAbsolutePath(), "autotester.log");
76     private static final File RESULTS_FILE = new File(Environment.getExternalStorageDirectory()
77             .getAbsolutePath(), "results.log");
78     private static final String GMAIL_PACKAGE_NAME = "com.google.android.gm";
79     private static final String DISABLE_COMMAND = "pm disable-user ";
80     private static final String ENABLE_COMMAND = "pm enable ";
81 
82     private UiDevice mDevice;
83     private List<String> mLaunchedPackages = new ArrayList<>();
84 
setUp()85     public void setUp() {
86         mDevice = UiDevice.getInstance(getInstrumentation());
87         try {
88             mDevice.setOrientationNatural();
89         } catch (RemoteException e) {
90             throw new RuntimeException("failed to freeze device orientaion", e);
91         }
92     }
93 
goHome()94     public void goHome() {
95         mDevice.pressHome();
96         mDevice.waitForIdle();
97     }
98 
99     @Override
tearDown()100     protected void tearDown() throws Exception {
101         mDevice.unfreezeRotation();
102         super.tearDown();
103     }
104 
populateRecentApps()105     public void populateRecentApps() throws IOException {
106         PackageManager pm = getInstrumentation().getContext().getPackageManager();
107         List<PackageInfo> packages = pm.getInstalledPackages(0);
108         mLaunchedPackages.clear();
109         for (PackageInfo pkg : packages) {
110             if (pkg.packageName.equals(getInstrumentation().getTargetContext().getPackageName())) {
111                 continue;
112             }
113             Intent intent = pm.getLaunchIntentForPackage(pkg.packageName);
114             if (intent == null) {
115                 continue;
116             }
117             intent.addCategory(Intent.CATEGORY_LAUNCHER);
118             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
119             getInstrumentation().getTargetContext().startActivity(intent);
120             SystemClock.sleep(5000);
121             mLaunchedPackages.add(pkg.packageName);
122         }
123 
124         // Close any crash dialogs
125         while (mDevice.hasObject(By.textContains("has stopped"))) {
126             mDevice.findObject(By.text("Close")).clickAndWait(Until.newWindow(), 2000);
127         }
128         TimeResultLogger.writeTimeStampLogStart(String.format("%s-%s",
129                 getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
130     }
131 
forceStopPackages(Bundle metrics)132     public void forceStopPackages(Bundle metrics) throws IOException {
133         TimeResultLogger.writeTimeStampLogEnd(String.format("%s-%s",
134                 getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
135         for (String pkg : mLaunchedPackages) {
136             try {
137                 mDevice.executeShellCommand("am force-stop " + pkg);
138             } catch (IOException e) {
139                 Log.w(LOG_TAG, "exeception while force stopping package " + pkg, e);
140             }
141         }
142         goHome();
143         TimeResultLogger.writeResultToFile(String.format("%s-%s",
144                 getClass().getSimpleName(), getName()), RESULTS_FILE, metrics);
145         super.afterTest(metrics);
146     }
147 
resetRecentsToBottom()148     public void resetRecentsToBottom() {
149         // Rather than trying to scroll back to the bottom, just re-open the recents list
150         mDevice.pressHome();
151         mDevice.waitForIdle();
152         try {
153             mDevice.pressRecentApps();
154         } catch (RemoteException e) {
155             throw new RuntimeException(e);
156         }
157         // use a long timeout to wait until recents populated
158         mDevice.wait(Until.findObject(RECENTS), 10000);
159         mDevice.waitForIdle();
160     }
161 
prepareNotifications()162     public void prepareNotifications() throws Exception {
163         blockNotifications();
164         goHome();
165         mDevice.openNotification();
166         SystemClock.sleep(100);
167         mDevice.waitForIdle();
168 
169         // CLEAR ALL might not be visible in case we don't have any clearable notifications.
170         UiObject clearAll =
171                 mDevice.findObject(new UiSelector().className(Button.class).text("CLEAR ALL"));
172         if (clearAll.exists()) {
173             clearAll.click();
174         }
175         mDevice.pressHome();
176         mDevice.waitForIdle();
177         Builder builder = new Builder(getInstrumentation().getTargetContext())
178                 .setContentTitle(NOTIFICATION_TEXT);
179         NotificationManager nm = (NotificationManager) getInstrumentation().getTargetContext()
180                 .getSystemService(Context.NOTIFICATION_SERVICE);
181         for (int icon : ICONS) {
182             builder.setContentText(Integer.toHexString(icon))
183                     .setSmallIcon(icon);
184             nm.notify(icon, builder.build());
185             SystemClock.sleep(100);
186         }
187         mDevice.waitForIdle();
188         TimeResultLogger.writeTimeStampLogStart(String.format("%s-%s",
189                 getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
190     }
191 
blockNotifications()192     public void blockNotifications() throws Exception {
193         mDevice.executeShellCommand(DISABLE_COMMAND + GMAIL_PACKAGE_NAME);
194     }
195 
unblockNotifications()196     public void unblockNotifications() throws Exception {
197         mDevice.executeShellCommand(ENABLE_COMMAND + GMAIL_PACKAGE_NAME);
198     }
199 
cancelNotifications(Bundle metrics)200     public void cancelNotifications(Bundle metrics) throws Exception {
201         unblockNotifications();
202         TimeResultLogger.writeTimeStampLogEnd(String.format("%s-%s",
203                 getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
204         NotificationManager nm = (NotificationManager) getInstrumentation().getTargetContext()
205                 .getSystemService(Context.NOTIFICATION_SERVICE);
206         nm.cancelAll();
207         TimeResultLogger.writeResultToFile(String.format("%s-%s",
208                 getClass().getSimpleName(), getName()), RESULTS_FILE, metrics);
209         super.afterTest(metrics);
210     }
211 
212     /** Starts from the bottom of the recent apps list and measures jank while flinging up. */
213     @JankTest(beforeTest = "populateRecentApps", beforeLoop = "resetRecentsToBottom",
214             afterTest = "forceStopPackages", expectedFrames = 100)
215     @GfxMonitor(processName = SYSTEMUI_PACKAGE)
testRecentAppsFling()216     public void testRecentAppsFling() {
217         UiObject2 recents = mDevice.findObject(RECENTS);
218         Rect r = recents.getVisibleBounds();
219         // decide the top & bottom edges for scroll gesture
220         int top = r.top + r.height() / 4; // top edge = top + 25% height
221         int bottom = r.bottom - 200; // bottom edge = bottom & shift up 200px
222         for (int i = 0; i < INNER_LOOP; i++) {
223             mDevice.swipe(r.width() / 2, top, r.width() / 2, bottom, DEFAULT_FLING_STEPS);
224             mDevice.waitForIdle();
225             mDevice.swipe(r.width() / 2, bottom, r.width() / 2, top, DEFAULT_FLING_STEPS);
226             mDevice.waitForIdle();
227         }
228     }
229 
openNotification()230     private void openNotification() {
231         mDevice.swipe(mDevice.getDisplayWidth() / 2,
232                 SWIPE_MARGIN, mDevice.getDisplayWidth() / 2,
233                 mDevice.getDisplayHeight() - SWIPE_MARGIN,
234                 DEFAULT_SCROLL_STEPS);
235     }
236 
closeNotification()237     private void closeNotification() {
238         mDevice.swipe(mDevice.getDisplayWidth() / 2,
239                 mDevice.getDisplayHeight() - SWIPE_MARGIN,
240                 mDevice.getDisplayWidth() / 2,
241                 SWIPE_MARGIN,
242                 DEFAULT_SCROLL_STEPS);
243     }
244 
245     /** Measures jank while pulling down the notification list */
246     @JankTest(expectedFrames = 100,
247             beforeTest = "prepareNotifications", afterTest = "cancelNotifications")
248     @GfxMonitor(processName = SYSTEMUI_PACKAGE)
testNotificationListPull()249     public void testNotificationListPull() {
250         for (int i = 0; i < INNER_LOOP; i++) {
251             openNotification();
252             mDevice.waitForIdle();
253             closeNotification();
254             mDevice.waitForIdle();
255         }
256     }
257 }
258 
259