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

Commit d11850cd authored by Nandana Dutt's avatar Nandana Dutt Committed by Narayan Kamath
Browse files

Add a privileged API for capturing and consuming bugreports

The API is mostly implemented; except for hooking up the listener
and handling an already running bugreport.

BugreportManager is the handle to the new API exposed to apps.

Generating bugreports requires root privileges. To limit the footprint
of the root access, the actual bugreport generation in Dumpstate binary,
is accessed as a oneshot service and dies after it finishes
running.

System server accesses Dumpstate via a binder interface since it does
not have root privileges.

Starting a oneshot service is done via setting a system property, which
needs to be done from system server. BugreportManagerService is the
new system server service that does this. BugreportManager calls into
BugreportManagerService via a binder interface, since the former is in
the app's process.

Both app to system server as well as system server to native service
calls are via implementations of IDumpstate binder interface.

Bug: 111441001
Test: builds. Flashed & verified it boots.
Test: wrote a test client (not included) and verified invoking
startBugreport works.

Change-Id: I4abeb753388c055c36ae0dd916af1ec8d40b7bf0
parent 2d917da0
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -120,6 +120,7 @@ import android.net.wifi.rtt.WifiRttManager;
import android.nfc.NfcManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.BugreportManager;
import android.os.Build;
import android.os.DeviceIdleManager;
import android.os.DropBoxManager;
@@ -127,6 +128,7 @@ import android.os.HardwarePropertiesManager;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.IDeviceIdleController;
import android.os.IDumpstate;
import android.os.IHardwarePropertiesManager;
import android.os.IPowerManager;
import android.os.IRecoverySystem;
@@ -1069,6 +1071,16 @@ final class SystemServiceRegistry {
                return new IncidentManager(ctx);
            }});

        registerService(Context.BUGREPORT_SERVICE, BugreportManager.class,
                new CachedServiceFetcher<BugreportManager>() {
                    @Override
                    public BugreportManager createService(ContextImpl ctx)
                            throws ServiceNotFoundException {
                        IBinder b = ServiceManager.getServiceOrThrow(Context.BUGREPORT_SERVICE);
                        return new BugreportManager(ctx.getOuterContext(),
                                IDumpstate.Stub.asInterface(b));
                    }});

        registerService(Context.AUTOFILL_MANAGER_SERVICE, AutofillManager.class,
                new CachedServiceFetcher<AutofillManager>() {
            @Override
+10 −0
Original line number Diff line number Diff line
@@ -4426,6 +4426,16 @@ public abstract class Context {
    @SystemApi
    public static final String STATS_MANAGER = "stats";

    /**
     * Service to capture a bugreport.
     * @see #getSystemService(String)
     * @see android.os.BugreportManager
     * @hide
     */
    // TODO: Expose API when the implementation is more complete.
    // @SystemApi
    public static final String BUGREPORT_SERVICE = "bugreport";

    /**
     * Use with {@link #getSystemService(String)} to retrieve a {@link
     * android.content.om.OverlayManager} for managing overlay packages.
+148 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.os;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.os.IBinder.DeathRecipient;

import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Class that provides a privileged API to capture and consume bugreports.
 *
 * @hide
 */
// TODO: Expose API when the implementation is more complete.
// @SystemApi
@SystemService(Context.BUGREPORT_SERVICE)
public class BugreportManager {
    private final Context mContext;
    private final IDumpstate mBinder;

    /** @hide */
    public BugreportManager(@NonNull Context context, IDumpstate binder) {
        mContext = context;
        mBinder = binder;
    }

    /**
     * An interface describing the listener for bugreport progress and status.
     */
    public interface BugreportListener {
        /**
         * Called when there is a progress update.
         * @param progress the progress in [0.0, 100.0]
         */
        void onProgress(float progress);

        @Retention(RetentionPolicy.SOURCE)
        @IntDef(prefix = { "BUGREPORT_ERROR_" }, value = {
                BUGREPORT_ERROR_INVALID_INPUT,
                BUGREPORT_ERROR_RUNTIME
        })

        /** Possible error codes taking a bugreport can encounter */
        @interface BugreportErrorCode {}

        /** The input options were invalid */
        int BUGREPORT_ERROR_INVALID_INPUT = 1;

        /** A runtime error occured */
        int BUGREPORT_ERROR_RUNTIME = 2;

        /**
         * Called when taking bugreport resulted in an error.
         *
         * @param errorCode the error that occurred. Possible values are
         *     {@code BUGREPORT_ERROR_INVALID_INPUT}, {@code BUGREPORT_ERROR_RUNTIME}.
         */
        void onError(@BugreportErrorCode int errorCode);

        /**
         * Called when taking bugreport finishes successfully
         *
         * @param durationMs time capturing bugreport took in milliseconds
         * @param title title for the bugreport; helpful in reminding the user why they took it
         * @param description detailed description for the bugreport
         */
        void onFinished(long durationMs, @NonNull String title,
                @NonNull String description);
    }

    /**
     * Starts a bugreport asynchronously.
     *
     * @param bugreportFd file to write the bugreport. This should be opened in write-only,
     *     append mode.
     * @param screenshotFd file to write the screenshot, if necessary. This should be opened
     *     in write-only, append mode.
     * @param params options that specify what kind of a bugreport should be taken
     * @param listener callback for progress and status updates
     */
    @RequiresPermission(android.Manifest.permission.DUMP)
    public void startBugreport(@NonNull FileDescriptor bugreportFd,
            @Nullable FileDescriptor screenshotFd,
            @NonNull BugreportParams params, @Nullable BugreportListener listener) {
        // TODO(b/111441001): Enforce android.Manifest.permission.DUMP if necessary.
        DumpstateListener dsListener = new DumpstateListener(listener);

        try {
            mBinder.startBugreport(bugreportFd, screenshotFd, params.getMode(), dsListener);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }


    // TODO(b/111441001) Connect up with BugreportListener methods.
    private final class DumpstateListener extends IDumpstateListener.Stub
            implements DeathRecipient {
        private final BugreportListener mListener;

        DumpstateListener(@Nullable BugreportListener listener) {
            mListener = listener;
        }

        @Override
        public void binderDied() {
            // TODO(b/111441001): implement
        }

        @Override
        public void onProgressUpdated(int progress) throws RemoteException {
            // TODO(b/111441001): implement
        }

        @Override
        public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
            // TODO(b/111441001): implement
        }

        @Override
        public void onSectionComplete(String title, int status, int size, int durationMs)
                throws RemoteException {
            // TODO(b/111441001): implement
        }
    }
}
+90 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.os;

import android.annotation.IntDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Parameters that specify what kind of bugreport should be taken.
 *
 * @hide
 */
// TODO: Expose API when the implementation is more complete.
// @SystemApi
public final class BugreportParams {
    private final int mMode;

    public BugreportParams(@BugreportMode int mode) {
        mMode = mode;
    }

    public int getMode() {
        return mMode;
    }

    /**
     * Defines acceptable types of bugreports.
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "BUGREPORT_MODE_" }, value = {
            BUGREPORT_MODE_FULL,
            BUGREPORT_MODE_INTERACTIVE,
            BUGREPORT_MODE_REMOTE,
            BUGREPORT_MODE_WEAR,
            BUGREPORT_MODE_TELEPHONY,
            BUGREPORT_MODE_WIFI
    })
    public @interface BugreportMode {}

    /**
     * Options for a bugreport without user interference (and hence causing less
     * interference to the system), but includes all sections.
     */
    public static final int BUGREPORT_MODE_FULL = IDumpstate.BUGREPORT_MODE_FULL;

    /**
     * Options that allow user to monitor progress and enter additional data; might not
     * include all sections.
     */
    public static final int BUGREPORT_MODE_INTERACTIVE = IDumpstate.BUGREPORT_MODE_INTERACTIVE;

    /**
     * Options for a bugreport requested remotely by administrator of the Device Owner app,
     * not the device's user.
     */
    public static final int BUGREPORT_MODE_REMOTE = IDumpstate.BUGREPORT_MODE_REMOTE;

    /**
     * Options for a bugreport on a wearable device.
     */
    public static final int BUGREPORT_MODE_WEAR = IDumpstate.BUGREPORT_MODE_WEAR;

    /**
     * Options for a lightweight version of bugreport that only includes a few, urgent
     * sections used to report telephony bugs.
     */
    public static final int BUGREPORT_MODE_TELEPHONY = IDumpstate.BUGREPORT_MODE_TELEPHONY;

    /**
     * Options for a lightweight bugreport that only includes a few sections related to
     * Wifi.
     */
    public static final int BUGREPORT_MODE_WIFI = IDumpstate.BUGREPORT_MODE_WIFI;
}
+43 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.server.os;

import android.content.Context;

import com.android.server.SystemService;

/**
 * Service that provides a privileged API to capture and consume bugreports.
 *
 * @hide
 */
public class BugreportManagerService extends SystemService {
    private static final String TAG = "BugreportManagerService";

    private BugreportManagerServiceImpl mService;

    public BugreportManagerService(Context context) {
        super(context);
    }

    @Override
    public void onStart() {
        mService = new BugreportManagerServiceImpl(getContext());
        // TODO(b/111441001): Needs sepolicy to be submitted first.
        // publishBinderService(Context.BUGREPORT_SERVICE, mService);
    }
}
Loading