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

Commit dc452e08 authored by Gavin Corkery's avatar Gavin Corkery
Browse files

Add retrieveBugreport API

Adds API support for deferring consent while
generating a bugreport, along with support for
the same caller to retrieve a bugreport that
was previously generated by them.

Test: atest CtsRootBugreportTestCases
Test: atest BugreportManagerServiceImplTest
Bug: 245328405
Change-Id: I6ea42fdc3609344aad3126c7af0a6f32666e7438
parent a2acce86
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -31900,9 +31900,11 @@ package android.os {
    method public void onEarlyReportFinished();
    method public void onError(int);
    method public void onFinished();
    method public void onFinished(@NonNull String);
    method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float);
    field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5
    field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1
    field public static final int BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE = 6; // 0x6
    field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2
    field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4
    field public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; // 0x3
+2 −0
Original line number Diff line number Diff line
@@ -9933,6 +9933,7 @@ package android.os {
  public final class BugreportManager {
    method @RequiresPermission(android.Manifest.permission.DUMP) @WorkerThread public void preDumpUiData();
    method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence);
    method @RequiresPermission(android.Manifest.permission.DUMP) @WorkerThread public void retrieveBugreport(@NonNull String, @NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
    method @RequiresPermission(android.Manifest.permission.DUMP) @WorkerThread public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
  }
@@ -9941,6 +9942,7 @@ package android.os {
    ctor public BugreportParams(int, int);
    method public int getFlags();
    method public int getMode();
    field public static final int BUGREPORT_FLAG_DEFER_CONSENT = 2; // 0x2
    field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1
    field public static final int BUGREPORT_MODE_FULL = 0; // 0x0
    field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1
+98 −10
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.os;

import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FloatRange;
import android.annotation.IntDef;
@@ -69,7 +70,10 @@ public final class BugreportManager {
     * An interface describing the callback for bugreport progress and status.
     *
     * <p>Callers will receive {@link #onProgress} calls as the bugreport progresses, followed by a
     * terminal call to either {@link #onFinished} or {@link #onError}.
     * terminal call to either {@link #onFinished} or {@link #onError}. Note that
     * {@link #onFinished(String)} will only be invoked when calling {@code startBugreport} with the
     * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} flag set. Otherwise,
     * {@link #onFinished()} will be invoked.
     *
     * <p>If an issue is encountered while starting the bugreport asynchronously, callers will
     * receive an {@link #onError} call without any {@link #onProgress} callbacks.
@@ -88,7 +92,8 @@ public final class BugreportManager {
                    BUGREPORT_ERROR_RUNTIME,
                    BUGREPORT_ERROR_USER_DENIED_CONSENT,
                    BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT,
                    BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS
                    BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS,
                    BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE
                })
        public @interface BugreportErrorCode {}

@@ -115,6 +120,10 @@ public final class BugreportManager {
        public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS =
                IDumpstateListener.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS;

        /** There is no bugreport to retrieve for the caller. */
        public static final int BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE =
                IDumpstateListener.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE;

        /**
         * Called when there is a progress update.
         *
@@ -137,9 +146,25 @@ public final class BugreportManager {
         */
        public void onError(@BugreportErrorCode int errorCode) {}

        /** Called when taking bugreport finishes successfully. */
        /** Called when taking bugreport finishes successfully.
         *
         * <p>This callback will be invoked if the
         * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} flag is not set. Otherwise, the
         * {@link #onFinished(String)} callback will be invoked.
         */
        public void onFinished() {}

        /** Called when taking bugreport finishes successfully.
         *
         * <p>This callback will only be invoked if the
         * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} flag is set. Otherwise, the
         * {@link #onFinished()} callback will be invoked.
         *
         * @param bugreportFile the absolute path of the generated bugreport file.

         */
        public void onFinished(@NonNull String bugreportFile) {}

        /**
         * Called when it is ready for calling app to show UI, showing any extra UI before this
         * callback can interfere with bugreport generation.
@@ -178,7 +203,9 @@ public final class BugreportManager {
     * updates.
     *
     * <p>The bugreport artifacts will be copied over to the given file descriptors only if the user
     * consents to sharing with the calling app.
     * consents to sharing with the calling app. If
     * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} is set, user consent will be deferred
     * and no files will be copied to the given file descriptors.
     *
     * <p>{@link BugreportManager} takes ownership of {@code bugreportFd} and {@code screenshotFd}.
     *
@@ -205,7 +232,9 @@ public final class BugreportManager {
            Preconditions.checkNotNull(executor);
            Preconditions.checkNotNull(callback);

            boolean isScreenshotRequested = screenshotFd != null;
            boolean deferConsent =
                    (params.getFlags() & BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT) != 0;
            boolean isScreenshotRequested = screenshotFd != null || deferConsent;
            if (screenshotFd == null) {
                // Binder needs a valid File Descriptor to be passed
                screenshotFd =
@@ -213,7 +242,7 @@ public final class BugreportManager {
                                new File("/dev/null"), ParcelFileDescriptor.MODE_READ_ONLY);
            }
            DumpstateListener dsListener =
                    new DumpstateListener(executor, callback, isScreenshotRequested);
                    new DumpstateListener(executor, callback, isScreenshotRequested, deferConsent);
            // Note: mBinder can get callingUid from the binder transaction.
            mBinder.startBugreport(
                    -1 /* callingUid */,
@@ -237,6 +266,58 @@ public final class BugreportManager {
        }
    }

    /**
     * Retrieves a previously generated bugreport.
     *
     * <p>The previously generated bugreport must have been generated by calling {@link
     * #startBugreport(ParcelFileDescriptor, ParcelFileDescriptor, BugreportParams,
     * Executor, BugreportCallback)} with the {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT}
     * flag set. The bugreport file returned by the {@link BugreportCallback#onFinished(String)}
     * callback for a previously generated bugreport must be passed to this method. A caller may
     * only retrieve bugreports that they have previously requested.
     *
     * <p>The bugreport artifacts will be copied over to the given file descriptor only if the user
     * consents to sharing with the calling app.
     *
     * <p>{@link BugreportManager} takes ownership of {@code bugreportFd} and {@code screenshotFd}.
     *
     * <p>The caller may only request to retrieve a given bugreport once. Subsequent calls will fail
     * with error code {@link BugreportCallback#BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE}.
     *
     * @param bugreportFile the identifier for a bugreport that was previously generated for this
     *      caller using {@code startBugreport}.
     * @param bugreportFd file to copy over the previous bugreport. This should be opened in
     *      write-only, append mode.
     * @param executor the executor to execute callback methods.
     * @param callback callback for progress and status updates.
     * @hide
     */
    @SystemApi
    @RequiresPermission(Manifest.permission.DUMP)
    @WorkerThread
    public void retrieveBugreport(
            @NonNull String bugreportFile,
            @NonNull ParcelFileDescriptor bugreportFd,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull BugreportCallback callback
    ) {
        try {
            Preconditions.checkNotNull(bugreportFile);
            Preconditions.checkNotNull(bugreportFd);
            Preconditions.checkNotNull(executor);
            Preconditions.checkNotNull(callback);
            DumpstateListener dsListener = new DumpstateListener(executor, callback, false, false);
            mBinder.retrieveBugreport(Binder.getCallingUid(), mContext.getOpPackageName(),
                    bugreportFd.getFileDescriptor(),
                    bugreportFile,
                    dsListener);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } finally {
            IoUtils.closeQuietly(bugreportFd);
        }
    }

    /**
     * Starts a connectivity bugreport.
     *
@@ -316,7 +397,7 @@ public final class BugreportManager {
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.DUMP)
    @RequiresPermission(Manifest.permission.DUMP)
    public void requestBugreport(
            @NonNull BugreportParams params,
            @Nullable CharSequence shareTitle,
@@ -335,12 +416,15 @@ public final class BugreportManager {
        private final Executor mExecutor;
        private final BugreportCallback mCallback;
        private final boolean mIsScreenshotRequested;
        private final boolean mIsConsentDeferred;

        DumpstateListener(
                Executor executor, BugreportCallback callback, boolean isScreenshotRequested) {
                Executor executor, BugreportCallback callback, boolean isScreenshotRequested,
                boolean isConsentDeferred) {
            mExecutor = executor;
            mCallback = callback;
            mIsScreenshotRequested = isScreenshotRequested;
            mIsConsentDeferred = isConsentDeferred;
        }

        @Override
@@ -364,10 +448,14 @@ public final class BugreportManager {
        }

        @Override
        public void onFinished() throws RemoteException {
        public void onFinished(String bugreportFile) throws RemoteException {
            final long identity = Binder.clearCallingIdentity();
            try {
                if (mIsConsentDeferred) {
                    mExecutor.execute(() -> mCallback.onFinished(bugreportFile));
                } else {
                    mExecutor.execute(() -> mCallback.onFinished());
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
+13 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.SystemApi;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;

/**
 * Parameters that specify what kind of bugreport should be taken.
@@ -125,7 +126,8 @@ public final class BugreportParams {
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = { "BUGREPORT_FLAG_" }, value = {
            BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA
            BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA,
            BUGREPORT_FLAG_DEFER_CONSENT
    })
    public @interface BugreportFlag {}

@@ -135,4 +137,14 @@ public final class BugreportParams {
     */
    public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA =
            IDumpstate.BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA;

    /**
     * Flag for deferring user consent.
     *
     * <p>This flag should be used in cases where it may not be possible for the user to respond
     * to a consent dialog immediately, such as when the user is driving. The generated bugreport
     * may be retrieved at a later time using {@link BugreportManager#retrieveBugreport(
     * String, ParcelFileDescriptor, Executor, BugreportManager.BugreportCallback)}.
     */
    public static final int BUGREPORT_FLAG_DEFER_CONSENT = IDumpstate.BUGREPORT_FLAG_DEFER_CONSENT;
}
+1 −1
Original line number Diff line number Diff line
@@ -677,7 +677,7 @@ public class BugreportReceiverTest {
        if (mScreenshotFd != null) {
            writeScreenshotFile(mScreenshotFd, SCREENSHOT_CONTENT);
        }
        mIDumpstateListener.onFinished();
        mIDumpstateListener.onFinished("");
        getInstrumentation().waitForIdleSync();
    }

Loading