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

Commit 5d51c314 authored by nathch's avatar nathch
Browse files

Dump information for all users running backup in backup dumpsys.

Currently backup dumpsys in bugreport only contains information
for system user making it hard to debug backup bugs in non-system
users. Therefore, add dumpsys information for all users running backup.

For system user, keep the dumpsys format the same for compatibility with
cts and gts tests. For non-system users, add a prefix "User <userid>:"
to all dumpsys headers.

The changes in Android.bp and AndroidManifest.xml are to support mocking
of the static method DumpUtils.checkDumpAndUsageStatsPermission in the
test testDump_systemUserFirst

Bug: 143867387

Test: atest com.android.server.backup.BackupManagerServiceTest

Test: atest com.android.server.backup.UserBackupManagerServiceTest

Test: adb shell pm create-user test1 -> say this gives 11
adb shell am start-user 11
adb shell bmgr --user 11 activate true
adb shell dumpsys backup users -> "Backup Manager is running for users: 0 11"
adb shell dumpsys backup -> contains both
"Backup Manager is enabled / setup complete / not pending init" and
"User 11:Backup Manager is disabled / not setup complete / not pending init"
adb shell pm remove-user 11

Test: "adb bugreport" on device with secondary user as created above and check that result
contains dumpsys for both system and secondary users.

Test: atest -v CtsBackupTestCases
Test: atest -v CtsBackupHostTestCases

Change-Id: Ib94c168f8e89b0ba8f398152ea744fe3d626efc4
parent e1319819
Loading
Loading
Loading
Loading
+21 −6
Original line number Diff line number Diff line
@@ -1452,6 +1452,11 @@ public class BackupManagerService extends IBackupManager.Stub {
        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
            return;
        }
        dumpWithoutCheckingPermission(fd, pw, args);
    }

    @VisibleForTesting
    void dumpWithoutCheckingPermission(FileDescriptor fd, PrintWriter pw, String[] args) {
        int userId = binderGetCallingUserId();
        if (!isUserReadyForBackup(userId)) {
            pw.println("Inactive");
@@ -1460,7 +1465,16 @@ public class BackupManagerService extends IBackupManager.Stub {

        if (args != null) {
            for (String arg : args) {
                if ("users".equals(arg.toLowerCase())) {
                if ("-h".equals(arg)) {
                    pw.println("'dumpsys backup' optional arguments:");
                    pw.println("  -h       : this help text");
                    pw.println("  a[gents] : dump information about defined backup agents");
                    pw.println("  transportclients : dump information about transport clients");
                    pw.println("  transportstats : dump transport statts");
                    pw.println("  users    : dump the list of users for which backup service "
                            + "is running");
                    return;
                } else if ("users".equals(arg.toLowerCase())) {
                    pw.print(DUMP_RUNNING_USERS_MESSAGE);
                    for (int i = 0; i < mUserServices.size(); i++) {
                        pw.print(" " + mUserServices.keyAt(i));
@@ -1471,13 +1485,14 @@ public class BackupManagerService extends IBackupManager.Stub {
            }
        }

        for (int i = 0; i < mUserServices.size(); i++) {
            UserBackupManagerService userBackupManagerService =
                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()");

                    getServiceForUserIfCallerHasPermission(mUserServices.keyAt(i), "dump()");
            if (userBackupManagerService != null) {
                userBackupManagerService.dump(fd, pw, args);
            }
        }
    }

    /**
     * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
+14 −19
Original line number Diff line number Diff line
@@ -3545,14 +3545,7 @@ public class UserBackupManagerService {
        try {
            if (args != null) {
                for (String arg : args) {
                    if ("-h".equals(arg)) {
                        pw.println("'dumpsys backup' optional arguments:");
                        pw.println("  -h       : this help text");
                        pw.println("  a[gents] : dump information about defined backup agents");
                        pw.println("  users    : dump the list of users for which backup service "
                                + "is running");
                        return;
                    } else if ("agents".startsWith(arg)) {
                    if ("agents".startsWith(arg)) {
                        dumpAgents(pw);
                        return;
                    } else if ("transportclients".equals(arg.toLowerCase())) {
@@ -3583,8 +3576,10 @@ public class UserBackupManagerService {
    }

    private void dumpInternal(PrintWriter pw) {
        // Add prefix for only non-system users so that system user dumpsys is the same as before
        String userPrefix = mUserId == UserHandle.USER_SYSTEM ? "" : "User " + mUserId + ":";
        synchronized (mQueueLock) {
            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
            pw.println(userPrefix + "Backup Manager is " + (mEnabled ? "enabled" : "disabled")
                    + " / " + (!mSetupComplete ? "not " : "") + "setup complete / "
                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
@@ -3594,13 +3589,13 @@ public class UserBackupManagerService {
                    + " (now = " + System.currentTimeMillis() + ')');
            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId));

            pw.println("Transport whitelist:");
            pw.println(userPrefix + "Transport whitelist:");
            for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
                pw.print("    ");
                pw.println(transport.flattenToShortString());
            }

            pw.println("Available transports:");
            pw.println(userPrefix + "Available transports:");
            final String[] transports = listAllTransports();
            if (transports != null) {
                for (String t : transports) {
@@ -3626,18 +3621,18 @@ public class UserBackupManagerService {

            mTransportManager.dumpTransportClients(pw);

            pw.println("Pending init: " + mPendingInits.size());
            pw.println(userPrefix + "Pending init: " + mPendingInits.size());
            for (String s : mPendingInits) {
                pw.println("    " + s);
            }

            pw.print("Ancestral: ");
            pw.print(userPrefix + "Ancestral: ");
            pw.println(Long.toHexString(mAncestralToken));
            pw.print("Current:   ");
            pw.print(userPrefix + "Current:   ");
            pw.println(Long.toHexString(mCurrentToken));

            int numPackages = mBackupParticipants.size();
            pw.println("Participants:");
            pw.println(userPrefix + "Participants:");
            for (int i = 0; i < numPackages; i++) {
                int uid = mBackupParticipants.keyAt(i);
                pw.print("  uid: ");
@@ -3648,7 +3643,7 @@ public class UserBackupManagerService {
                }
            }

            pw.println("Ancestral packages: "
            pw.println(userPrefix + "Ancestral packages: "
                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
            if (mAncestralPackages != null) {
                for (String pkg : mAncestralPackages) {
@@ -3657,17 +3652,17 @@ public class UserBackupManagerService {
            }

            Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
            pw.println("Ever backed up: " + processedPackages.size());
            pw.println(userPrefix + "Ever backed up: " + processedPackages.size());
            for (String pkg : processedPackages) {
                pw.println("    " + pkg);
            }

            pw.println("Pending key/value backup: " + mPendingBackups.size());
            pw.println(userPrefix + "Pending key/value backup: " + mPendingBackups.size());
            for (BackupRequest req : mPendingBackups.values()) {
                pw.println("    " + req);
            }

            pw.println("Full backup queue:" + mFullBackupQueue.size());
            pw.println(userPrefix + "Full backup queue:" + mFullBackupQueue.size());
            for (FullBackupEntry entry : mFullBackupQueue) {
                pw.print("    ");
                pw.print(entry.lastBackup);
+40 −1
Original line number Diff line number Diff line
@@ -81,7 +81,10 @@ import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;

import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;

/**
@@ -1238,13 +1241,49 @@ public class UserBackupManagerServiceTest {
        assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber2);
    }

    /**
     * Test that {@link UserBackupManagerService#dump()} for system user does not prefix dump with
     * "User 0:".
     */
    @Test
    public void testDump_forSystemUser_DoesNotHaveUserPrefix() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService service =
                BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
                        UserHandle.USER_SYSTEM,
                        mContext,
                        mBackupThread,
                        mBaseStateDir,
                        mDataDir,
                        mTransportManager);

        StringWriter dump = new StringWriter();
        service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]);

        assertThat(dump.toString()).startsWith("Backup Manager is ");
    }

    /**
     * Test that {@link UserBackupManagerService#dump()} for non-system user prefixes dump with
     * "User <userid>:".
     */
    @Test
    public void testDump_forNonSystemUser_HasUserPrefix() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();

        StringWriter dump = new StringWriter();
        service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]);

        assertThat(dump.toString()).startsWith("User " + USER_ID + ":" + "Backup Manager is ");
    }

    private File createTestFile() throws IOException {
        File testFile = new File(mContext.getFilesDir(), "test");
        testFile.createNewFile();
        return testFile;
    }


    /**
     * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
     * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
+25 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -59,6 +60,7 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@@ -83,6 +85,8 @@ public class BackupManagerServiceTest {
    @Mock
    private UserBackupManagerService mUserBackupManagerService;
    @Mock
    private UserBackupManagerService mNonSystemUserBackupManagerService;
    @Mock
    private Context mContextMock;
    @Mock
    private PrintWriter mPrintWriterMock;
@@ -105,7 +109,7 @@ public class BackupManagerServiceTest {

        mUserServices = new SparseArray<>();
        mUserServices.append(UserHandle.USER_SYSTEM, mUserBackupManagerService);
        mUserServices.append(NON_USER_SYSTEM, mUserBackupManagerService);
        mUserServices.append(NON_USER_SYSTEM, mNonSystemUserBackupManagerService);

        when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
        when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
@@ -512,6 +516,26 @@ public class BackupManagerServiceTest {
        mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);

        verifyNoMoreInteractions(mUserBackupManagerService);
        verifyNoMoreInteractions(mNonSystemUserBackupManagerService);
    }

    /**
     * Test that {@link BackupManagerService#dump()} dumps system user information before non-system
     * user information.
     */

    @Test
    public void testDump_systemUserFirst() {
        String[] args = new String[0];
        mService.dumpWithoutCheckingPermission(mFileDescriptorStub, mPrintWriterMock, args);

        InOrder inOrder =
                inOrder(mUserBackupManagerService, mNonSystemUserBackupManagerService);
        inOrder.verify(mUserBackupManagerService)
                .dump(mFileDescriptorStub, mPrintWriterMock, args);
        inOrder.verify(mNonSystemUserBackupManagerService)
                .dump(mFileDescriptorStub, mPrintWriterMock, args);
        inOrder.verifyNoMoreInteractions();
    }

    @Test