Merge changes from topic "bugreporting-aosp-sync" am: 8fafabfed1 am: 111f411617
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1440641 Change-Id: Ibe5a6f75de4c8a4e3916bb468f1203beba99a76a
This commit is contained in:
@@ -20,7 +20,11 @@ android_test {
|
|||||||
"android.test.runner",
|
"android.test.runner",
|
||||||
"android.test.base",
|
"android.test.base",
|
||||||
],
|
],
|
||||||
static_libs: ["androidx.test.rules", "truth-prebuilt"],
|
static_libs: [
|
||||||
|
"androidx.test.rules",
|
||||||
|
"androidx.test.uiautomator_uiautomator",
|
||||||
|
"truth-prebuilt",
|
||||||
|
],
|
||||||
test_suites: ["general-tests"],
|
test_suites: ["general-tests"],
|
||||||
sdk_version: "test_current",
|
sdk_version: "test_current",
|
||||||
platform_apis: true,
|
platform_apis: true,
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ package com.android.os.bugreports.tests;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
@@ -25,17 +27,27 @@ import android.content.Context;
|
|||||||
import android.os.BugreportManager;
|
import android.os.BugreportManager;
|
||||||
import android.os.BugreportManager.BugreportCallback;
|
import android.os.BugreportManager.BugreportCallback;
|
||||||
import android.os.BugreportParams;
|
import android.os.BugreportParams;
|
||||||
|
import android.os.FileUtils;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.StrictMode;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.test.InstrumentationRegistry;
|
import androidx.test.InstrumentationRegistry;
|
||||||
|
import androidx.test.filters.LargeTest;
|
||||||
|
import androidx.test.uiautomator.By;
|
||||||
|
import androidx.test.uiautomator.BySelector;
|
||||||
|
import androidx.test.uiautomator.UiDevice;
|
||||||
|
import androidx.test.uiautomator.UiObject2;
|
||||||
|
import androidx.test.uiautomator.Until;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExternalResource;
|
||||||
import org.junit.rules.TestName;
|
import org.junit.rules.TestName;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
@@ -51,9 +63,11 @@ import java.util.concurrent.TimeUnit;
|
|||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class BugreportManagerTest {
|
public class BugreportManagerTest {
|
||||||
@Rule public TestName name = new TestName();
|
@Rule public TestName name = new TestName();
|
||||||
|
@Rule public ExtendedStrictModeVmPolicy mTemporaryVmPolicy = new ExtendedStrictModeVmPolicy();
|
||||||
|
|
||||||
private static final String TAG = "BugreportManagerTest";
|
private static final String TAG = "BugreportManagerTest";
|
||||||
private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(10);
|
private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(10);
|
||||||
|
private static final long UIAUTOMATOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
|
||||||
|
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
private Executor mExecutor;
|
private Executor mExecutor;
|
||||||
@@ -86,6 +100,8 @@ public class BugreportManagerTest {
|
|||||||
@After
|
@After
|
||||||
public void teardown() throws Exception {
|
public void teardown() throws Exception {
|
||||||
dropPermissions();
|
dropPermissions();
|
||||||
|
FileUtils.closeQuietly(mBugreportFd);
|
||||||
|
FileUtils.closeQuietly(mScreenshotFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -95,47 +111,45 @@ public class BugreportManagerTest {
|
|||||||
// wifi bugreport does not take screenshot
|
// wifi bugreport does not take screenshot
|
||||||
mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, wifi(),
|
mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, wifi(),
|
||||||
mExecutor, callback);
|
mExecutor, callback);
|
||||||
|
shareConsentDialog(ConsentReply.ALLOW);
|
||||||
waitTillDoneOrTimeout(callback);
|
waitTillDoneOrTimeout(callback);
|
||||||
|
|
||||||
assertThat(callback.isDone()).isTrue();
|
assertThat(callback.isDone()).isTrue();
|
||||||
// Wifi bugreports should not receive any progress.
|
// Wifi bugreports should not receive any progress.
|
||||||
assertThat(callback.hasReceivedProgress()).isFalse();
|
assertThat(callback.hasReceivedProgress()).isFalse();
|
||||||
// TODO: Because of b/130234145, consent dialog is not shown; so we get a timeout error.
|
assertThat(mBugreportFile.length()).isGreaterThan(0L);
|
||||||
// When the bug is fixed, accept consent via UIAutomator and verify contents
|
|
||||||
// of mBugreportFd.
|
|
||||||
assertThat(callback.getErrorCode()).isEqualTo(
|
|
||||||
BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
|
|
||||||
assertFdsAreClosed(mBugreportFd);
|
assertFdsAreClosed(mBugreportFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void normalFlow_interactive() throws Exception {
|
public void normalFlow_interactive() throws Exception {
|
||||||
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
||||||
// interactive bugreport does not take screenshot
|
// interactive bugreport does not take screenshot
|
||||||
mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, interactive(),
|
mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, interactive(),
|
||||||
mExecutor, callback);
|
mExecutor, callback);
|
||||||
|
shareConsentDialog(ConsentReply.ALLOW);
|
||||||
waitTillDoneOrTimeout(callback);
|
waitTillDoneOrTimeout(callback);
|
||||||
|
|
||||||
assertThat(callback.isDone()).isTrue();
|
assertThat(callback.isDone()).isTrue();
|
||||||
// Interactive bugreports show progress updates.
|
// Interactive bugreports show progress updates.
|
||||||
assertThat(callback.hasReceivedProgress()).isTrue();
|
assertThat(callback.hasReceivedProgress()).isTrue();
|
||||||
assertThat(callback.getErrorCode()).isEqualTo(
|
assertThat(mBugreportFile.length()).isGreaterThan(0L);
|
||||||
BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
|
|
||||||
assertFdsAreClosed(mBugreportFd);
|
assertFdsAreClosed(mBugreportFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void normalFlow_full() throws Exception {
|
public void normalFlow_full() throws Exception {
|
||||||
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
||||||
mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback);
|
mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback);
|
||||||
|
shareConsentDialog(ConsentReply.ALLOW);
|
||||||
waitTillDoneOrTimeout(callback);
|
waitTillDoneOrTimeout(callback);
|
||||||
|
|
||||||
assertThat(callback.isDone()).isTrue();
|
assertThat(callback.isDone()).isTrue();
|
||||||
assertThat(callback.getErrorCode()).isEqualTo(
|
// bugreport and screenshot files shouldn't be empty when user consents.
|
||||||
BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
|
assertThat(mBugreportFile.length()).isGreaterThan(0L);
|
||||||
// bugreport and screenshot files should be empty when user consent timed out.
|
assertThat(mScreenshotFile.length()).isGreaterThan(0L);
|
||||||
assertThat(mBugreportFile.length()).isEqualTo(0);
|
|
||||||
assertThat(mScreenshotFile.length()).isEqualTo(0);
|
|
||||||
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
|
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +158,8 @@ public class BugreportManagerTest {
|
|||||||
// Start bugreport #1
|
// Start bugreport #1
|
||||||
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
||||||
mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
|
mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
|
||||||
|
// TODO(b/162389762) Make sure the wait time is reasonable
|
||||||
|
shareConsentDialog(ConsentReply.ALLOW);
|
||||||
|
|
||||||
// Before #1 is done, try to start #2.
|
// Before #1 is done, try to start #2.
|
||||||
assertThat(callback.isDone()).isFalse();
|
assertThat(callback.isDone()).isFalse();
|
||||||
@@ -375,4 +391,88 @@ public class BugreportManagerTest {
|
|||||||
private static BugreportParams full() {
|
private static BugreportParams full() {
|
||||||
return new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL);
|
return new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Allow/deny the consent dialog to sharing bugreport data or check existence only. */
|
||||||
|
private enum ConsentReply {
|
||||||
|
ALLOW,
|
||||||
|
DENY,
|
||||||
|
TIMEOUT
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the consent dialog is shown and take action according to <code>consentReply<code/>.
|
||||||
|
* It will fail if the dialog is not shown when <code>ignoreNotFound<code/> is false.
|
||||||
|
*/
|
||||||
|
private void shareConsentDialog(@NonNull ConsentReply consentReply) throws Exception {
|
||||||
|
mTemporaryVmPolicy.permitIncorrectContextUse();
|
||||||
|
final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
|
||||||
|
|
||||||
|
// Unlock before finding/clicking an object.
|
||||||
|
device.wakeUp();
|
||||||
|
device.executeShellCommand("wm dismiss-keyguard");
|
||||||
|
|
||||||
|
final BySelector consentTitleObj = By.res("android", "alertTitle");
|
||||||
|
if (!device.wait(Until.hasObject(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS)) {
|
||||||
|
fail("The consent dialog is not found");
|
||||||
|
}
|
||||||
|
if (consentReply.equals(ConsentReply.TIMEOUT)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final BySelector selector;
|
||||||
|
if (consentReply.equals(ConsentReply.ALLOW)) {
|
||||||
|
selector = By.res("android", "button1");
|
||||||
|
Log.d(TAG, "Allow the consent dialog");
|
||||||
|
} else { // ConsentReply.DENY
|
||||||
|
selector = By.res("android", "button2");
|
||||||
|
Log.d(TAG, "Deny the consent dialog");
|
||||||
|
}
|
||||||
|
final UiObject2 btnObj = device.findObject(selector);
|
||||||
|
assertNotNull("The button of consent dialog is not found", btnObj);
|
||||||
|
btnObj.click();
|
||||||
|
|
||||||
|
Log.d(TAG, "Wait for the dialog to be dismissed");
|
||||||
|
assertTrue(device.wait(Until.gone(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A rule to change strict mode vm policy temporarily till test method finished.
|
||||||
|
*
|
||||||
|
* To permit the non-visual context usage in tests while taking bugreports need user consent,
|
||||||
|
* or UiAutomator/BugreportManager.DumpstateListener would run into error.
|
||||||
|
* UiDevice#findObject creates UiObject2, its Gesture object and ViewConfiguration and
|
||||||
|
* UiObject2#click need to know bounds. Both of them access to WindowManager internally without
|
||||||
|
* visual context comes from InstrumentationRegistry and violate the policy.
|
||||||
|
* Also <code>DumpstateListener<code/> violate the policy when onScreenshotTaken is called.
|
||||||
|
*
|
||||||
|
* TODO(b/161201609) Remove this class once violations fixed.
|
||||||
|
*/
|
||||||
|
static class ExtendedStrictModeVmPolicy extends ExternalResource {
|
||||||
|
private boolean mWasVmPolicyChanged = false;
|
||||||
|
private StrictMode.VmPolicy mOldVmPolicy;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void after() {
|
||||||
|
restoreVmPolicyIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void permitIncorrectContextUse() {
|
||||||
|
// Allow to call multiple times without losing old policy.
|
||||||
|
if (mOldVmPolicy == null) {
|
||||||
|
mOldVmPolicy = StrictMode.getVmPolicy();
|
||||||
|
}
|
||||||
|
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
||||||
|
.detectAll()
|
||||||
|
.permitIncorrectContextUse()
|
||||||
|
.penaltyLog()
|
||||||
|
.build());
|
||||||
|
mWasVmPolicyChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restoreVmPolicyIfNeeded() {
|
||||||
|
if (mWasVmPolicyChanged && mOldVmPolicy != null) {
|
||||||
|
StrictMode.setVmPolicy(mOldVmPolicy);
|
||||||
|
mOldVmPolicy = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user