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

Commit 4fe5d002 authored by Nikhil Kumar's avatar Nikhil Kumar
Browse files

Refactor Bug report flow to work for all ADMIN users

With the existing implementation only the primary user is allowed
to take bug report and approve authorization for bug and inicdent
reports to be taken.

With hsum (Headlesss SYSTEM user mode) implementation primary user will always run in background. we need to refactor the bugreport flow so that it can be triggered by any admin user.

The following components has been refactored to allow any ADMIN user to trigger the bugreport, see progress notification, pass user inputs in case of interactive bugreport and share the bugreport. Though the actual bugreport (dumpstate) will always run on user 0 only the user visible components and components responsible to trigger the bugreport and track progress ex- shell, any approved bundled app like betterbug will run on the current foreground admin user.

1. ActivityManagerService.requestBugReportWithDescription has been
modified to send BUGREPORT_REQUESTED broadcast to the caller user.
2. BugReportManagerServiceImpl has been modified to enable
any admin user to initiate the bug report.
3. BugReportProgressService has been modified to send BUGREPORT_FINISHED
broadcast to the shell context user.
4. IncidentCompanionService and PendingReports has been modifed to let
any admin user provide feedback and authorization for bug and inicdent
reports to be taken.

Bug: 256583570
Test: atest BugreportManagerTest
Test: Manually tested that admin users are allowed to take bug report.

Change-Id: Ib22775e7ad998369404cf15cdc220c9b4019b296
parent cb699221
Loading
Loading
Loading
Loading
+1 −2
Original line number Original line Diff line number Diff line
@@ -455,8 +455,7 @@ public class BugreportProgressService extends Service {
        intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
        intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
        intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_NONCE, nonce);
        intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_NONCE, nonce);
        intent.putExtra(EXTRA_BUGREPORT, bugreportFileName);
        intent.putExtra(EXTRA_BUGREPORT, bugreportFileName);
        context.sendBroadcastAsUser(intent, UserHandle.SYSTEM,
        context.sendBroadcast(intent, android.Manifest.permission.DUMP);
                android.Manifest.permission.DUMP);
    }
    }


    /**
    /**
+2 −1
Original line number Original line Diff line number Diff line
@@ -7391,10 +7391,11 @@ public class ActivityManagerService extends IActivityManager.Stub
        if (shareDescription != null) {
        if (shareDescription != null) {
            triggerShellBugreport.putExtra(EXTRA_DESCRIPTION, shareDescription);
            triggerShellBugreport.putExtra(EXTRA_DESCRIPTION, shareDescription);
        }
        }
        UserHandle callingUser = Binder.getCallingUserHandle();
        final long identity = Binder.clearCallingIdentity();
        final long identity = Binder.clearCallingIdentity();
        try {
        try {
            // Send broadcast to shell to trigger bugreport using Bugreport API
            // Send broadcast to shell to trigger bugreport using Bugreport API
            mContext.sendBroadcastAsUser(triggerShellBugreport, UserHandle.SYSTEM);
            mContext.sendBroadcastAsUser(triggerShellBugreport, callingUser);
        } finally {
        } finally {
            Binder.restoreCallingIdentity(identity);
            Binder.restoreCallingIdentity(identity);
        }
        }
+14 −22
Original line number Original line Diff line number Diff line
@@ -34,7 +34,6 @@ import android.os.IncidentManager;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.util.Log;


import com.android.internal.util.DumpUtils;
import com.android.internal.util.DumpUtils;
@@ -128,21 +127,21 @@ public class IncidentCompanionService extends SystemService {
            try {
            try {
                final Context context = getContext();
                final Context context = getContext();


                final int primaryUser = getAndValidateUser(context);
                // Get the current admin user. Only they can do incident reports.
                if (primaryUser == UserHandle.USER_NULL) {
                final int currentAdminUser = getCurrentUserIfAdmin();
                if (currentAdminUser == UserHandle.USER_NULL) {
                    return;
                    return;
                }
                }


                final Intent intent = new Intent(Intent.ACTION_INCIDENT_REPORT_READY);
                final Intent intent = new Intent(Intent.ACTION_INCIDENT_REPORT_READY);
                intent.setComponent(new ComponentName(pkg, cls));
                intent.setComponent(new ComponentName(pkg, cls));


                Log.d(TAG, "sendReportReadyBroadcast sending primaryUser=" + primaryUser
                Log.d(TAG, "sendReportReadyBroadcast sending currentUser=" + currentAdminUser
                        + " userHandle=" + UserHandle.getUserHandleForUid(primaryUser)
                        + " userHandle=" + UserHandle.of(currentAdminUser)
                        + " intent=" + intent);
                        + " intent=" + intent);


                // Send it to the primary user.  Only they can do incident reports.
                context.sendBroadcastAsUserMultiplePermissions(intent,
                context.sendBroadcastAsUserMultiplePermissions(intent,
                        UserHandle.getUserHandleForUid(primaryUser),
                        UserHandle.of(currentAdminUser),
                        DUMP_AND_USAGE_STATS_PERMISSIONS);
                        DUMP_AND_USAGE_STATS_PERMISSIONS);
            } finally {
            } finally {
                Binder.restoreCallingIdentity(ident);
                Binder.restoreCallingIdentity(ident);
@@ -414,10 +413,10 @@ public class IncidentCompanionService extends SystemService {
    }
    }


    /**
    /**
     * Check whether the current user is the primary user, and return the user id if they are.
     * Check whether the current user is an admin user, and return the user id if they are.
     * Returns UserHandle.USER_NULL if not valid.
     * Returns UserHandle.USER_NULL if not valid.
     */
     */
    public static int getAndValidateUser(Context context) {
    public static int getCurrentUserIfAdmin() {
        // Current user
        // Current user
        UserInfo currentUser;
        UserInfo currentUser;
        try {
        try {
@@ -427,28 +426,21 @@ public class IncidentCompanionService extends SystemService {
            throw new RuntimeException(ex);
            throw new RuntimeException(ex);
        }
        }


        // Primary user
        final UserManager um = UserManager.get(context);
        final UserInfo primaryUser = um.getPrimaryUser();

        // Check that we're using the right user.
        // Check that we're using the right user.
        if (currentUser == null) {
        if (currentUser == null) {
            Log.w(TAG, "No current user.  Nobody to approve the report."
            Log.w(TAG, "No current user.  Nobody to approve the report."
                    + " The report will be denied.");
                    + " The report will be denied.");
            return UserHandle.USER_NULL;
            return UserHandle.USER_NULL;
        }
        }
        if (primaryUser == null) {

            Log.w(TAG, "No primary user.  Nobody to approve the report."
        if (!currentUser.isAdmin()) {
            Log.w(TAG, "Only an admin user running in foreground can approve "
                    + "bugreports, but the current foreground user is not an admin user. "
                    + "The report will be denied.");
                    + "The report will be denied.");
            return UserHandle.USER_NULL;
            return UserHandle.USER_NULL;
        }
        }
        if (primaryUser.id != currentUser.id) {
            Log.w(TAG, "Only the primary user can approve bugreports, but they are not"
                    + " the current user. The report will be denied.");
            return UserHandle.USER_NULL;
        }


        return primaryUser.id;
        return currentUser.id;
    }
    }
}
}
+28 −23
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.server.incident;
package com.android.server.incident;


import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.BroadcastOptions;
import android.content.ComponentName;
import android.content.ComponentName;
@@ -272,15 +273,19 @@ class PendingReports {
            return;
            return;
        }
        }


        // Find the primary user of this device.
        // Find the current user of the device and check if they are an admin.
        final int primaryUser = getAndValidateUser();
        final int currentAdminUser = getCurrentUserIfAdmin();
        if (primaryUser == UserHandle.USER_NULL) {

        // Deny the report if the current admin user is null
        // or not the user who requested the report.
        if (currentAdminUser == UserHandle.USER_NULL
                || currentAdminUser != UserHandle.getUserId(callingUid)) {
            denyReportBeforeAddingRec(listener, callingPackage);
            denyReportBeforeAddingRec(listener, callingPackage);
            return;
            return;
        }
        }


        // Find the approver app (hint: it's PermissionController).
        // Find the approver app (hint: it's PermissionController).
        final ComponentName receiver = getApproverComponent(primaryUser);
        final ComponentName receiver = getApproverComponent(currentAdminUser);
        if (receiver == null) {
        if (receiver == null) {
            // We couldn't find an approver... so deny the request here and now, before we
            // We couldn't find an approver... so deny the request here and now, before we
            // do anything else.
            // do anything else.
@@ -298,26 +303,26 @@ class PendingReports {
        try {
        try {
            listener.asBinder().linkToDeath(() -> {
            listener.asBinder().linkToDeath(() -> {
                Log.i(TAG, "Got death notification listener=" + listener);
                Log.i(TAG, "Got death notification listener=" + listener);
                cancelReportImpl(listener, receiver, primaryUser);
                cancelReportImpl(listener, receiver, currentAdminUser);
            }, 0);
            }, 0);
        } catch (RemoteException ex) {
        } catch (RemoteException ex) {
            Log.e(TAG, "Remote died while trying to register death listener: " + rec.getUri());
            Log.e(TAG, "Remote died while trying to register death listener: " + rec.getUri());
            // First, remove from our list.
            // First, remove from our list.
            cancelReportImpl(listener, receiver, primaryUser);
            cancelReportImpl(listener, receiver, currentAdminUser);
        }
        }


        // Go tell Permission controller to start asking the user.
        // Go tell Permission controller to start asking the user.
        sendBroadcast(receiver, primaryUser);
        sendBroadcast(receiver, currentAdminUser);
    }
    }


    /**
    /**
     * Cancel a pending report request (because of an explicit call to cancel)
     * Cancel a pending report request (because of an explicit call to cancel)
     */
     */
    private void cancelReportImpl(IIncidentAuthListener listener) {
    private void cancelReportImpl(IIncidentAuthListener listener) {
        final int primaryUser = getAndValidateUser();
        final int currentAdminUser = getCurrentUserIfAdmin();
        final ComponentName receiver = getApproverComponent(primaryUser);
        final ComponentName receiver = getApproverComponent(currentAdminUser);
        if (primaryUser != UserHandle.USER_NULL && receiver != null) {
        if (currentAdminUser != UserHandle.USER_NULL && receiver != null) {
            cancelReportImpl(listener, receiver, primaryUser);
            cancelReportImpl(listener, receiver, currentAdminUser);
        }
        }
    }
    }


@@ -326,13 +331,13 @@ class PendingReports {
     * by the calling app, or because of a binder death).
     * by the calling app, or because of a binder death).
     */
     */
    private void cancelReportImpl(IIncidentAuthListener listener, ComponentName receiver,
    private void cancelReportImpl(IIncidentAuthListener listener, ComponentName receiver,
            int primaryUser) {
            @UserIdInt int user) {
        // First, remove from our list.
        // First, remove from our list.
        synchronized (mLock) {
        synchronized (mLock) {
            removePendingReportRecLocked(listener);
            removePendingReportRecLocked(listener);
        }
        }
        // Second, call back to PermissionController to say it's canceled.
        // Second, call back to PermissionController to say it's canceled.
        sendBroadcast(receiver, primaryUser);
        sendBroadcast(receiver, user);
    }
    }


    /**
    /**
@@ -342,21 +347,21 @@ class PendingReports {
     * cleanup cases to keep the apps' list in sync with ours.
     * cleanup cases to keep the apps' list in sync with ours.
     */
     */
    private void sendBroadcast() {
    private void sendBroadcast() {
        final int primaryUser = getAndValidateUser();
        final int currentAdminUser = getCurrentUserIfAdmin();
        if (primaryUser == UserHandle.USER_NULL) {
        if (currentAdminUser == UserHandle.USER_NULL) {
            return;
            return;
        }
        }
        final ComponentName receiver = getApproverComponent(primaryUser);
        final ComponentName receiver = getApproverComponent(currentAdminUser);
        if (receiver == null) {
        if (receiver == null) {
            return;
            return;
        }
        }
        sendBroadcast(receiver, primaryUser);
        sendBroadcast(receiver, currentAdminUser);
    }
    }


    /**
    /**
     * Send the confirmation broadcast.
     * Send the confirmation broadcast.
     */
     */
    private void sendBroadcast(ComponentName receiver, int primaryUser) {
    private void sendBroadcast(ComponentName receiver, int currentUser) {
        final Intent intent = new Intent(Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED);
        final Intent intent = new Intent(Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED);
        intent.setComponent(receiver);
        intent.setComponent(receiver);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -364,8 +369,8 @@ class PendingReports {
        final BroadcastOptions options = BroadcastOptions.makeBasic();
        final BroadcastOptions options = BroadcastOptions.makeBasic();
        options.setBackgroundActivityStartsAllowed(true);
        options.setBackgroundActivityStartsAllowed(true);


        // Send it to the primary user.
        // Send it to the current user.
        mContext.sendBroadcastAsUser(intent, UserHandle.getUserHandleForUid(primaryUser),
        mContext.sendBroadcastAsUser(intent, UserHandle.of(currentUser),
                android.Manifest.permission.APPROVE_INCIDENT_REPORTS, options.toBundle());
                android.Manifest.permission.APPROVE_INCIDENT_REPORTS, options.toBundle());
    }
    }


@@ -420,11 +425,11 @@ class PendingReports {
    }
    }


    /**
    /**
     * Check whether the current user is the primary user, and return the user id if they are.
     * Check whether the current user is an admin user, and return the user id if they are.
     * Returns UserHandle.USER_NULL if not valid.
     * Returns UserHandle.USER_NULL if not valid.
     */
     */
    private int getAndValidateUser() {
    private int getCurrentUserIfAdmin() {
        return IncidentCompanionService.getAndValidateUser(mContext);
        return IncidentCompanionService.getCurrentUserIfAdmin();
    }
    }


    /**
    /**
+9 −12
Original line number Original line Diff line number Diff line
@@ -33,8 +33,8 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.Slog;
import android.util.Slog;


@@ -189,10 +189,10 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
    }
    }


    /**
    /**
     * Validates that the current user is the primary user or when bugreport is requested remotely
     * Validates that the current user is an admin user or, when bugreport is requested remotely
     * and current user is affiliated user.
     * that the current user is an affiliated user.
     *
     *
     * @throws IllegalArgumentException if the current user is not the primary user
     * @throws IllegalArgumentException if the current user is not an admin user
     */
     */
    private void ensureUserCanTakeBugReport(int bugreportMode) {
    private void ensureUserCanTakeBugReport(int bugreportMode) {
        UserInfo currentUser = null;
        UserInfo currentUser = null;
@@ -202,20 +202,17 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
            // Impossible to get RemoteException for an in-process call.
            // Impossible to get RemoteException for an in-process call.
        }
        }


        UserInfo primaryUser = UserManager.get(mContext).getPrimaryUser();
        if (currentUser == null) {
        if (currentUser == null) {
            logAndThrow("No current user. Only primary user is allowed to take bugreports.");
            logAndThrow("There is no current user, so no bugreport can be requested.");
        }
        }
        if (primaryUser == null) {

            logAndThrow("No primary user. Only primary user is allowed to take bugreports.");
        if (!currentUser.isAdmin()) {
        }
        if (primaryUser.id != currentUser.id) {
            if (bugreportMode == BugreportParams.BUGREPORT_MODE_REMOTE
            if (bugreportMode == BugreportParams.BUGREPORT_MODE_REMOTE
                    && isCurrentUserAffiliated(currentUser.id)) {
                    && isCurrentUserAffiliated(currentUser.id)) {
                return;
                return;
            }
            }
            logAndThrow("Current user not primary user. Only primary user"
            logAndThrow(TextUtils.formatSimple("Current user %s is not an admin user."
                    + " is allowed to take bugreports.");
                    + " Only admin users are allowed to take bugreport.", currentUser.id));
        }
        }
    }
    }