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