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