blob: 8f6cdc027e76acc4fb80ab49bf0a6e99028c3f09 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test.util.dismissdialogs;
import android.app.Activity;
import android.app.Instrumentation;
import android.os.Bundle;
import android.os.Environment;
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.helpers.ChromeHelperImpl;
import android.platform.test.helpers.GmailHelperImpl;
import android.platform.test.helpers.GoogleCameraHelperImpl;
import android.platform.test.helpers.GoogleKeyboardHelperImpl;
import android.platform.test.helpers.IStandardAppHelper;
import android.platform.test.helpers.MapsHelperImpl;
import android.platform.test.helpers.PhotosHelperImpl;
import android.platform.test.helpers.PlayMoviesHelperImpl;
import android.platform.test.helpers.PlayMusicHelperImpl;
import android.platform.test.helpers.PlayStoreHelperImpl;
import android.platform.test.helpers.YouTubeHelperImpl;
import android.support.test.InstrumentationRegistry;
import android.support.test.aupt.UiWatchers;
import android.support.test.launcherhelper.ILauncherStrategy;
import android.support.test.launcherhelper.LauncherStrategyFactory;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.lang.IllegalAccessException;
import java.lang.InstantiationException;
import java.lang.NoSuchMethodException;
import java.lang.ReflectiveOperationException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
/**
* A utility to dismiss all predictable, relevant one-time dialogs
*/
public class DismissDialogsInstrumentation extends Instrumentation {
private static final String LOG_TAG = DismissDialogsInstrumentation.class.getSimpleName();
private static final String IMAGE_SUBFOLDER = "dialog-dismissal";
private static final long INIT_TIMEOUT = 20000;
private static final long MAX_INIT_RETRIES = 5;
// Comma-separated value indicating for which apps to dismiss dialogs
private static final String PARAM_APP = "apps";
// Boolean to indicate if this should take screenshots to document dismissal
private static final String PARAM_SCREENSHOTS = "screenshots";
// Boolean to indicate if this should quit if any failure occurs
private static final String PARAM_QUIT_ON_ERROR = "quitOnError";
// Key for status bundles provided when running the preparer
private static final String BUNDLE_DISMISSED_APP_KEY = "dismissedApp";
private static final String BUNDLE_APP_ERROR_KEY = "appError";
private Map<String, Class<? extends IStandardAppHelper>> mKeyHelperMap;
private String[] mApps;
private boolean mScreenshots;
private boolean mQuitOnError;
private UiDevice mDevice;
@Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
mKeyHelperMap = new HashMap<String, Class<? extends IStandardAppHelper>>();
mKeyHelperMap.put("Chrome", ChromeHelperImpl.class);
mKeyHelperMap.put("GoogleCamera", GoogleCameraHelperImpl.class);
mKeyHelperMap.put("GoogleKeyboard", GoogleKeyboardHelperImpl.class);
mKeyHelperMap.put("Gmail", GmailHelperImpl.class);
mKeyHelperMap.put("Maps", MapsHelperImpl.class);
mKeyHelperMap.put("Photos", PhotosHelperImpl.class);
mKeyHelperMap.put("PlayMovies", PlayMoviesHelperImpl.class);
mKeyHelperMap.put("PlayMusic", PlayMusicHelperImpl.class);
mKeyHelperMap.put("PlayStore", PlayStoreHelperImpl.class);
//mKeyHelperMap.put("Settings", SettingsHelperImpl.class);
mKeyHelperMap.put("YouTube", YouTubeHelperImpl.class);
String appsString = arguments.getString(PARAM_APP);
if (appsString == null) {
throw new IllegalArgumentException("Missing 'apps' parameter.");
}
mApps = appsString.split(",");
String screenshotsString = arguments.getString(PARAM_SCREENSHOTS);
if (screenshotsString == null) {
Log.i(LOG_TAG, "No 'screenshots' parameter. Defaulting to true.");
mScreenshots = true;
} else {
mScreenshots = "true".equals(screenshotsString);
}
String quitString = arguments.getString(PARAM_QUIT_ON_ERROR);
if (quitString == null) {
Log.i(LOG_TAG, "No 'quitOnError' parameter. Defaulting to quit on error.");
mQuitOnError = true;
} else {
mQuitOnError = "true".equals(quitString);
}
start();
}
@Override
public void onStart() {
super.onStart();
InstrumentationRegistry.registerInstance(this, new Bundle());
mDevice = UiDevice.getInstance(this);
UiWatchers watcherManager = new UiWatchers();
watcherManager.registerAnrAndCrashWatchers(this);
takeScreenDump("init", "pre-setup");
try {
mDevice.setOrientationNatural();
} catch (RemoteException e) {
Log.e(LOG_TAG, "Unable to set device orientation.", e);
}
for (int retry = 1; retry <= MAX_INIT_RETRIES; retry++) {
ILauncherStrategy launcherStrategy =
LauncherStrategyFactory.getInstance(mDevice).getLauncherStrategy();
boolean foundHome = mDevice.wait(Until.hasObject(
launcherStrategy.getWorkspaceSelector()), INIT_TIMEOUT);
if (foundHome) {
sendStatusUpdate(Activity.RESULT_OK, "launcher");
break;
} else {
takeScreenDump("init", String.format("launcher-selection-failure-%d", retry));
if (retry == MAX_INIT_RETRIES && mQuitOnError) {
throw new RuntimeException("Unable to select launcher workspace. Quitting.");
} else {
sendStatusUpdate(Activity.RESULT_CANCELED, "launcher");
Log.e(LOG_TAG, "Failed to find home selector; try #" + retry);
// HACK: Try to poke at UI to fix accessibility issue (b/21448825)
try {
mDevice.sleep();
SystemClock.sleep(1000);
mDevice.wakeUp();
mDevice.pressMenu();
UiDevice.getInstance(this).pressHome();
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to avoid UI bug b/21448825.", e);
}
}
}
}
for (String app : mApps) {
Log.i(LOG_TAG, String.format("Dismissing dialogs for app, %s.", app));
try {
if (!dismissDialogs(app)) {
throw new IllegalArgumentException(
String.format("Unrecognized app \"%s\"", mApps));
} else {
sendStatusUpdate(Activity.RESULT_OK, app);
}
} catch (ReflectiveOperationException e) {
if (mQuitOnError) {
quitWithError(app, e);
} else {
sendStatusUpdate(Activity.RESULT_CANCELED, app);
Log.w(LOG_TAG, "ReflectiveOperationException. Continuing with dismissal.", e);
}
} catch (RuntimeException e) {
if (mQuitOnError) {
quitWithError(app, e);
} else {
sendStatusUpdate(Activity.RESULT_CANCELED, app);
Log.w(LOG_TAG, "RuntimeException. Continuing with dismissal.", e);
}
} catch (AssertionError e) {
if (mQuitOnError) {
quitWithError(app, new Exception(e));
} else {
sendStatusUpdate(Activity.RESULT_CANCELED, app);
Log.w(LOG_TAG, "AssertionError. Continuing with dismissal.", e);
}
}
// Always return to the home page after dismissal
UiDevice.getInstance(this).pressHome();
}
watcherManager.removeAnrAndCrashWatchers(this);
finish(Activity.RESULT_OK, new Bundle());
}
private boolean dismissDialogs(String app) throws NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
try {
if (mKeyHelperMap.containsKey(app)) {
Class<? extends IStandardAppHelper> appHelperClass = mKeyHelperMap.get(app);
IStandardAppHelper helper =
appHelperClass.getDeclaredConstructor(Instrumentation.class).newInstance(this);
takeScreenDump(app, "-dialog1-pre-open");
helper.open();
takeScreenDump(app, "-dialog2-pre-dismissal");
helper.dismissInitialDialogs();
takeScreenDump(app, "-dialog3-post-dismissal");
helper.exit();
takeScreenDump(app, "-dialog4-post-exit");
return true;
} else {
return false;
}
} catch (Exception | AssertionError e) {
takeScreenDump(app, "-exception");
throw e;
}
}
private void sendStatusUpdate(int code, String app) {
Bundle result = new Bundle();
result.putString(BUNDLE_DISMISSED_APP_KEY, app);
sendStatus(code, result);
}
private void quitWithError(String app, Exception exception) {
Log.e(LOG_TAG, "Quitting with exception.", exception);
// Pass Bundle with debugging information to TF
Bundle result = new Bundle();
result.putString(BUNDLE_DISMISSED_APP_KEY, app);
result.putString(BUNDLE_APP_ERROR_KEY, exception.toString());
finish(Activity.RESULT_CANCELED, result);
}
private void takeScreenDump(String app, String suffix) {
if (!mScreenshots) {
return;
}
try {
File dir = new File(Environment.getExternalStorageDirectory(), IMAGE_SUBFOLDER);
if (!dir.exists() && !dir.mkdirs()) {
throw new RuntimeException(String.format(
"Unable to create or find directory, %s.", dir.getPath()));
}
File scr = new File(dir, "dd-" + app + suffix + ".png");
File uix = new File(dir, "dd-" + app + suffix + ".uix");
Log.v(LOG_TAG, String.format("Screen file path: %s", scr.getPath()));
Log.v(LOG_TAG, String.format("UI XML file path: %s", uix.getPath()));
scr.createNewFile();
uix.createNewFile();
UiDevice.getInstance(this).takeScreenshot(scr);
UiDevice.getInstance(this).dumpWindowHierarchy(uix);
} catch (IOException e) {
Log.e(LOG_TAG, "Failed screen dump.", e);
}
}
}