1 /* 2 * Copyright (C) 2018 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 com.android.server.wm.flicker.helpers; 18 19 import static android.os.SystemClock.sleep; 20 import static android.view.Surface.ROTATION_0; 21 22 import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 23 24 import static org.junit.Assert.assertNotNull; 25 26 import android.content.Context; 27 import android.content.pm.PackageManager; 28 import android.graphics.Point; 29 import android.graphics.Rect; 30 import android.os.RemoteException; 31 import android.support.test.uiautomator.By; 32 import android.support.test.uiautomator.BySelector; 33 import android.support.test.uiautomator.Configurator; 34 import android.support.test.uiautomator.UiDevice; 35 import android.support.test.uiautomator.UiObject2; 36 import android.support.test.uiautomator.Until; 37 import android.util.Log; 38 import android.util.Rational; 39 import android.view.Surface; 40 import android.view.View; 41 import android.view.ViewConfiguration; 42 43 import androidx.test.InstrumentationRegistry; 44 45 import com.android.server.wm.flicker.WindowUtils; 46 47 import java.util.Locale; 48 49 /** Collection of UI Automation helper functions. */ 50 public class AutomationUtils { 51 private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; 52 private static final long FIND_TIMEOUT = 10000; 53 private static final long LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout() * 2L; 54 private static final String TAG = "FLICKER"; 55 wakeUpAndGoToHomeScreen()56 public static void wakeUpAndGoToHomeScreen() { 57 UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 58 try { 59 device.wakeUp(); 60 } catch (RemoteException e) { 61 throw new RuntimeException(e); 62 } 63 device.pressHome(); 64 } 65 66 /** 67 * Sets {@link android.app.UiAutomation#waitForIdle(long, long)} global timeout to 0 causing the 68 * {@link android.app.UiAutomation#waitForIdle(long, long)} function to timeout instantly. This 69 * removes some delays when using the UIAutomator library required to create fast UI 70 * transitions. 71 */ setFastWait()72 public static void setFastWait() { 73 Configurator.getInstance().setWaitForIdleTimeout(0); 74 } 75 76 /** Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior. */ setDefaultWait()77 public static void setDefaultWait() { 78 Configurator.getInstance().setWaitForIdleTimeout(10000); 79 } 80 isQuickstepEnabled(UiDevice device)81 public static boolean isQuickstepEnabled(UiDevice device) { 82 boolean enabled = device.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")) == null; 83 Log.d(TAG, "Quickstep enabled: " + enabled); 84 return enabled; 85 } 86 openQuickstep(UiDevice device)87 public static void openQuickstep(UiDevice device) { 88 if (isQuickstepEnabled(device)) { 89 int height = device.getDisplayHeight(); 90 UiObject2 navBar = device.findObject(By.res(SYSTEMUI_PACKAGE, "navigation_bar_frame")); 91 92 Rect navBarVisibleBounds; 93 94 // TODO(vishnun) investigate why this object cannot be found. 95 if (navBar != null) { 96 navBarVisibleBounds = navBar.getVisibleBounds(); 97 } else { 98 Log.e(TAG, "Could not find nav bar, infer location"); 99 navBarVisibleBounds = WindowUtils.getNavigationBarPosition(ROTATION_0); 100 } 101 102 // Swipe from nav bar to 2/3rd down the screen. 103 device.swipe( 104 navBarVisibleBounds.centerX(), 105 navBarVisibleBounds.centerY(), 106 navBarVisibleBounds.centerX(), 107 height * 2 / 3, 108 (navBarVisibleBounds.centerY() - height * 2 / 3) / 100); // 100 px/step 109 } 110 111 // use a long timeout to wait until recents populated 112 BySelector recentsSysUISelector = By.res(device.getLauncherPackageName(), "overview_panel"); 113 UiObject2 recents = device.wait(Until.findObject(recentsSysUISelector), FIND_TIMEOUT); 114 115 // Quickstep detection is flaky on AOSP, UIDevice doesn't always find SysUI elements 116 // If it couldn't find, try pressing 'recent items' button 117 if (recents == null) { 118 try { 119 device.pressRecentApps(); 120 } catch (RemoteException e) { 121 throw new RuntimeException(e); 122 } 123 recents = device.wait(Until.findObject(recentsSysUISelector), FIND_TIMEOUT); 124 } 125 126 assertNotNull("Recent items didn't appear", recents); 127 device.waitForIdle(); 128 } 129 clearRecents(UiDevice device)130 public static void clearRecents(UiDevice device) { 131 if (isQuickstepEnabled(device)) { 132 openQuickstep(device); 133 134 for (int i = 0; i < 10; i++) { 135 device.swipe( 136 device.getDisplayWidth() / 2, 137 device.getDisplayHeight() / 2, 138 device.getDisplayWidth(), 139 device.getDisplayHeight() / 2, 140 5); 141 142 BySelector noRecentItemsSelector = 143 getLauncherOverviewSelector(device).desc("No recent items"); 144 UiObject2 noRecentItems = device.wait(Until.findObject(noRecentItemsSelector), 100); 145 146 // If "No recent items" is displayed, there're no apps to remove 147 if (noRecentItems != null) { 148 return; 149 } 150 151 // If "Clear all" button appears, use it 152 BySelector clearAllSelector = 153 By.res(device.getLauncherPackageName(), "clear_all_button"); 154 UiObject2 clearAllButton = device.wait(Until.findObject(clearAllSelector), 100); 155 if (clearAllButton != null) { 156 clearAllButton.click(); 157 return; 158 } 159 } 160 } 161 } 162 getLauncherOverviewSelector(UiDevice device)163 private static BySelector getLauncherOverviewSelector(UiDevice device) { 164 return By.res(device.getLauncherPackageName(), "overview_panel"); 165 } 166 longPressRecents(UiDevice device)167 private static void longPressRecents(UiDevice device) { 168 BySelector recentsSelector = By.res(SYSTEMUI_PACKAGE, "recent_apps"); 169 UiObject2 recentsButton = device.wait(Until.findObject(recentsSelector), FIND_TIMEOUT); 170 assertNotNull("Unable to find 'recent items' button", recentsButton); 171 recentsButton.click(LONG_PRESS_TIMEOUT); 172 } 173 launchSplitScreen(UiDevice device)174 public static void launchSplitScreen(UiDevice device) { 175 if (isQuickstepEnabled(device)) { 176 // Quickstep enabled 177 openQuickstep(device); 178 } else { 179 try { 180 device.pressRecentApps(); 181 } catch (RemoteException e) { 182 Log.e(TAG, "launchSplitScreen", e); 183 } 184 } 185 186 BySelector overviewIconSelector = 187 By.res(device.getLauncherPackageName(), "icon").clazz(View.class); 188 UiObject2 overviewIcon = device.wait(Until.findObject(overviewIconSelector), FIND_TIMEOUT); 189 assertNotNull("Unable to find app icon in Overview", overviewIcon); 190 overviewIcon.click(); 191 192 BySelector splitScreenButtonSelector = By.text("Split screen"); 193 UiObject2 splitScreenButton = 194 device.wait(Until.findObject(splitScreenButtonSelector), FIND_TIMEOUT); 195 assertNotNull("Unable to find Split screen button in Overview", splitScreenButton); 196 splitScreenButton.click(); 197 198 // Wait for animation to complete. 199 sleep(2000); 200 201 UiObject2 divider = 202 device.wait(Until.findObject(getSplitScreenDividerSelector()), FIND_TIMEOUT); 203 assertNotNull("Unable to find Split screen divider", divider); 204 } 205 getSplitScreenDividerSelector()206 private static BySelector getSplitScreenDividerSelector() { 207 return By.res(SYSTEMUI_PACKAGE, "docked_divider_handle"); 208 } 209 exitSplitScreen(UiDevice device)210 public static void exitSplitScreen(UiDevice device) { 211 // Quickstep enabled 212 UiObject2 divider = 213 device.wait(Until.findObject(getSplitScreenDividerSelector()), FIND_TIMEOUT); 214 assertNotNull("Unable to find Split screen divider", divider); 215 216 // Drag the split screen divider to the top of the screen 217 int rotation = device.getDisplayRotation(); 218 boolean isRotated = rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270; 219 220 Point dstPoint; 221 if (isRotated) { 222 dstPoint = new Point(0, device.getDisplayWidth() / 2); 223 } else { 224 dstPoint = new Point(device.getDisplayWidth() / 2, 0); 225 } 226 divider.drag(dstPoint, 400); 227 // Wait for animation to complete. 228 sleep(2000); 229 } 230 resizeSplitScreen(UiDevice device, Rational windowHeightRatio)231 public static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) { 232 BySelector dividerSelector = getSplitScreenDividerSelector(); 233 UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT); 234 assertNotNull("Unable to find Split screen divider", divider); 235 236 int destHeight = 237 (int) (WindowUtils.getDisplayBounds().height() * windowHeightRatio.floatValue()); 238 239 // Drag the split screen divider to so that the ratio of top window height and bottom 240 // window height is windowHeightRatio 241 device.drag( 242 divider.getVisibleBounds().centerX(), 243 divider.getVisibleBounds().centerY(), 244 device.getDisplayWidth() / 2, 245 destHeight, 246 10); 247 // divider.drag(new Point(device.getDisplayWidth() / 2, destHeight), 400) 248 device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT); 249 250 // Wait for animation to complete. 251 sleep(2000); 252 } 253 getPipWindowSelector()254 public static BySelector getPipWindowSelector() { 255 return By.res(SYSTEMUI_PACKAGE, "background"); 256 } 257 closePipWindow(UiDevice device)258 public static void closePipWindow(UiDevice device) { 259 UiObject2 pipWindow = device.findObject(getPipWindowSelector()); 260 assertNotNull("PIP window not found", pipWindow); 261 262 pipWindow.click(); 263 264 UiObject2 exitPipObject = device.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss")); 265 assertNotNull("PIP window dismiss button not found", pipWindow); 266 267 exitPipObject.click(); 268 // Wait for animation to complete. 269 sleep(2000); 270 } 271 expandPipWindow(UiDevice device)272 public static void expandPipWindow(UiDevice device) { 273 UiObject2 pipWindow = device.findObject(getPipWindowSelector()); 274 assertNotNull("PIP window not found", pipWindow); 275 pipWindow.click(); 276 pipWindow.click(); 277 } 278 stopPackage(Context context, String packageName)279 public static void stopPackage(Context context, String packageName) { 280 runShellCommand("am force-stop " + packageName); 281 int packageUid; 282 try { 283 packageUid = context.getPackageManager().getPackageUid(packageName, /* flags= */ 0); 284 } catch (PackageManager.NameNotFoundException e) { 285 return; 286 } 287 while (targetPackageIsRunning(packageUid)) { 288 try { 289 Thread.sleep(100); 290 } catch (InterruptedException e) { 291 // ignore 292 } 293 } 294 } 295 targetPackageIsRunning(int uid)296 private static boolean targetPackageIsRunning(int uid) { 297 final String result = 298 runShellCommand( 299 String.format(Locale.getDefault(), "cmd activity get-uid-state %d", uid)); 300 return !result.contains("(NONEXISTENT)"); 301 } 302 } 303