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

Commit 7053e28b authored by Chandan Nath's avatar Chandan Nath
Browse files

[Multi-user] Clean up user state stored in the system user directory

when user is removed.

For non system users, backup state is stored in both the user's own dir and the system dir.
When the user is removed, the user's own dir gets removed by the OS. This code change ensures
that the part of the user backup state which is in the system dir also gets removed.

Bug: 127650374

Test: atest -v CtsBackupHostTestCases:android.cts.backup.MultiUserBackupStateTest

Change-Id: I4ea252e8e6da608e36ec3ac335666923d88a8748
parent be8b2e85
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -112,6 +112,11 @@ public class Bmgr {
            return;
        }

        if ("activated".equals(op)) {
            doActivated(userId);
            return;
        }

        if (!isBackupActive(userId)) {
            return;
        }
@@ -200,6 +205,21 @@ public class Bmgr {
        return true;
    }

    private String activatedToString(boolean activated) {
        return activated ? "activated" : "deactivated";
    }

    private void doActivated(@UserIdInt int userId) {
        try {
            System.out.println("Backup Manager currently "
                    + activatedToString(mBmgr.isBackupServiceActive(userId)));
        } catch (RemoteException e) {
            System.err.println(e.toString());
            System.err.println(BMGR_NOT_RUNNING_ERR);
        }

    }

    private String enableToString(boolean enabled) {
        return enabled ? "enabled" : "disabled";
    }
@@ -907,6 +927,7 @@ public class Bmgr {
        System.err.println("       bmgr cancel backups");
        System.err.println("       bmgr init TRANSPORT...");
        System.err.println("       bmgr activate BOOL");
        System.err.println("       bmgr activated");
        System.err.println("");
        System.err.println("The '--user' option specifies the user on which the operation is run.");
        System.err.println("It must be the first argument before the operation.");
@@ -978,6 +999,9 @@ public class Bmgr {
        System.err.println("If the argument is 'true' it will be activated, otherwise it will be");
        System.err.println("deactivated. When deactivated, the service will not be running and no");
        System.err.println("operations can be performed until activation.");
        System.err.println("");
        System.err.println("The 'activated' command reports the current activated/deactivated");
        System.err.println("state of the backup mechanism.");
    }

    private static class BackupMonitor extends IBackupManagerMonitor.Stub {
+33 −0
Original line number Diff line number Diff line
@@ -31,11 +31,14 @@ import android.app.backup.ISelectBackupTransportCallback;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.FileUtils;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -49,6 +52,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemConfig;
import com.android.server.SystemService;

import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
@@ -86,6 +90,18 @@ public class BackupManagerService {

    private Set<ComponentName> mTransportWhitelist;

    private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                if (userId > 0) { // for only non system users
                    onRemovedNonSystemUser(userId);
                }
            }
        }
    };

    /** Instantiate a new instance of {@link BackupManagerService}. */
    public BackupManagerService(
            Context context, Trampoline trampoline, HandlerThread backupThread) {
@@ -99,6 +115,23 @@ public class BackupManagerService {
        if (mTransportWhitelist == null) {
            mTransportWhitelist = Collections.emptySet();
        }

        mContext.registerReceiver(mUserRemovedReceiver,
                new IntentFilter(Intent.ACTION_USER_REMOVED));
    }

    /**
     * Remove backup state for non system {@code userId} when the user is removed from the device.
     * For non system users, backup state is stored in both the user's own dir and the system dir.
     * When the user is removed, the user's own dir gets removed by the OS. This method ensures that
     * the part of the user backup state which is in the system dir also gets removed.
     */
    private void onRemovedNonSystemUser(int userId) {
        Slog.i(TAG, "Removing state for non system user " + userId);
        File dir = UserBackupManagerFiles.getStateDirInSystemDir(userId);
        if (!FileUtils.deleteContentsAndDir(dir)) {
            Slog.w(TAG, "Failed to delete state dir for removed user: " + userId);
        }
    }

    /**
+9 −6
Original line number Diff line number Diff line
@@ -47,7 +47,6 @@ import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.server.backup.utils.FileUtils;
import com.android.server.backup.utils.RandomAccessFileUtils;

import java.io.File;
@@ -95,7 +94,7 @@ public class Trampoline extends IBackupManager.Stub {
     * Name of file for non-system users that remembers whether backup was explicitly activated or
     * deactivated with a call to setBackupServiceActive.
     */
    private static final String REMEMBER_ACTIVATED_FILENAME_PREFIX = "backup-remember-activated";
    private static final String REMEMBER_ACTIVATED_FILENAME = "backup-remember-activated";

    // Product-level suppression of backup/restore.
    private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
@@ -143,8 +142,7 @@ public class Trampoline extends IBackupManager.Stub {

    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
    protected File getRememberActivatedFileForNonSystemUser(int userId) {
        return FileUtils.createNewFile(UserBackupManagerFiles.getStateFileInSystemDir(
                REMEMBER_ACTIVATED_FILENAME_PREFIX, userId));
        return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId);
    }

    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
@@ -336,8 +334,13 @@ public class Trampoline extends IBackupManager.Stub {
        // action since we need to remember that a permissioned call was made irrespective of
        // whether the call changes the state or not.
        if (userId != UserHandle.USER_SYSTEM) {
            RandomAccessFileUtils.writeBoolean(getRememberActivatedFileForNonSystemUser(userId),
                    makeActive);
            try {
                File rememberFile = getRememberActivatedFileForNonSystemUser(userId);
                createFile(rememberFile);
                RandomAccessFileUtils.writeBoolean(rememberFile, makeActive);
            } catch (IOException e) {
                Slog.e(TAG, "Unable to persist backup service activity", e);
            }
        }

        if (mGlobalDisable) {
+8 −3
Original line number Diff line number Diff line
@@ -49,8 +49,13 @@ final class UserBackupManagerFiles {
        return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
    }

    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
    static File getStateFileInSystemDir(String prefix, int userId) {
        return new File(getBaseStateDir(UserHandle.USER_SYSTEM), prefix + "-" + userId);
    /** A user specific dir within the system user's directory. */
    static File getStateDirInSystemDir(int userId) {
        return new File(getBaseStateDir(UserHandle.USER_SYSTEM), "" + userId);
    }

    /** Stored in a user specific dir within the system user's directory. */
    static File getStateFileInSystemDir(String filename, int userId) {
        return new File(getStateDirInSystemDir(userId), filename);
    }
}