Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c51591c authored by Gavin Corkery's avatar Gavin Corkery Committed by Automerger Merge Worker
Browse files

Merge changes from topic "bugreporting-aosp-sync" am: 8fafabfe am: 111f4116

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1440641

Change-Id: Ibe5a6f75de4c8a4e3916bb468f1203beba99a76a
parents 8fa94f4a 111f4116
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -20,7 +20,11 @@ android_test {
        "android.test.runner",
        "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"],
    sdk_version: "test_current",
    platform_apis: true,
+114 −14
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.os.bugreports.tests;

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 android.Manifest;
@@ -25,17 +27,27 @@ import android.content.Context;
import android.os.BugreportManager;
import android.os.BugreportManager.BugreportCallback;
import android.os.BugreportParams;
import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.StrictMode;
import android.util.Log;

import androidx.annotation.NonNull;
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.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -51,9 +63,11 @@ import java.util.concurrent.TimeUnit;
@RunWith(JUnit4.class)
public class BugreportManagerTest {
    @Rule public TestName name = new TestName();
    @Rule public ExtendedStrictModeVmPolicy mTemporaryVmPolicy = new ExtendedStrictModeVmPolicy();

    private static final String TAG = "BugreportManagerTest";
    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 Executor mExecutor;
@@ -86,6 +100,8 @@ public class BugreportManagerTest {
    @After
    public void teardown() throws Exception {
        dropPermissions();
        FileUtils.closeQuietly(mBugreportFd);
        FileUtils.closeQuietly(mScreenshotFd);
    }


@@ -95,47 +111,45 @@ public class BugreportManagerTest {
        // wifi bugreport does not take screenshot
        mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, wifi(),
                mExecutor, callback);
        shareConsentDialog(ConsentReply.ALLOW);
        waitTillDoneOrTimeout(callback);

        assertThat(callback.isDone()).isTrue();
        // Wifi bugreports should not receive any progress.
        assertThat(callback.hasReceivedProgress()).isFalse();
        // TODO: Because of b/130234145, consent dialog is not shown; so we get a timeout error.
        // 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);
        assertThat(mBugreportFile.length()).isGreaterThan(0L);
        assertFdsAreClosed(mBugreportFd);
    }

    @LargeTest
    @Test
    public void normalFlow_interactive() throws Exception {
        BugreportCallbackImpl callback = new BugreportCallbackImpl();
        // interactive bugreport does not take screenshot
        mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, interactive(),
                mExecutor, callback);

        shareConsentDialog(ConsentReply.ALLOW);
        waitTillDoneOrTimeout(callback);

        assertThat(callback.isDone()).isTrue();
        // Interactive bugreports show progress updates.
        assertThat(callback.hasReceivedProgress()).isTrue();
        assertThat(callback.getErrorCode()).isEqualTo(
                BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
        assertThat(mBugreportFile.length()).isGreaterThan(0L);
        assertFdsAreClosed(mBugreportFd);
    }

    @LargeTest
    @Test
    public void normalFlow_full() throws Exception {
        BugreportCallbackImpl callback = new BugreportCallbackImpl();
        mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback);

        shareConsentDialog(ConsentReply.ALLOW);
        waitTillDoneOrTimeout(callback);

        assertThat(callback.isDone()).isTrue();
        assertThat(callback.getErrorCode()).isEqualTo(
                BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
        // bugreport and screenshot files should be empty when user consent timed out.
        assertThat(mBugreportFile.length()).isEqualTo(0);
        assertThat(mScreenshotFile.length()).isEqualTo(0);
        // bugreport and screenshot files shouldn't be empty when user consents.
        assertThat(mBugreportFile.length()).isGreaterThan(0L);
        assertThat(mScreenshotFile.length()).isGreaterThan(0L);
        assertFdsAreClosed(mBugreportFd, mScreenshotFd);
    }

@@ -144,6 +158,8 @@ public class BugreportManagerTest {
        // Start bugreport #1
        BugreportCallbackImpl callback = new BugreportCallbackImpl();
        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.
        assertThat(callback.isDone()).isFalse();
@@ -375,4 +391,88 @@ public class BugreportManagerTest {
    private static BugreportParams 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;
            }
        }
    }
}