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