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

Commit d1df67ef authored by Bernardo Rufino's avatar Bernardo Rufino
Browse files

Eagerly initialize BMS in Trampoline's constructor

After multi-user BMS became a lightweight class as well, so no need to
lazily initialize it. This lays the ground for unifying Trampoline and
BMS.

Test: atest TrampolineTest
Test: adb shell bmgr backupnow <package>
Bug: 135247112
Bug: 135661048
Change-Id: Ia7f69d2ed282c6dfe6443a601f27229d43802fe6
parent f128bcc4
Loading
Loading
Loading
Loading
+32 −18
Original line number Diff line number Diff line
@@ -195,16 +195,20 @@ public class BackupManagerService {
    }

    boolean isAbleToServeUser(int userId) {
        return getServiceUsers().get(UserHandle.USER_SYSTEM) != null
                && getServiceUsers().get(userId) != null;
        return getUserServices().get(UserHandle.USER_SYSTEM) != null
                && getUserServices().get(userId) != null;
    }

    /**
     *  Returns a lst of users currently unlocked that have a
     *  {@link UserBackupManagerService} registered.
     *  Returns a list of users currently unlocked that have a {@link UserBackupManagerService}
     *  registered.
     *
     *  Warning: Do NOT modify returned object as it's used inside.
     *
     *  TODO: Return a copy or only expose read-only information through other means.
     */
    @VisibleForTesting
    public SparseArray<UserBackupManagerService> getServiceUsers() {
    public SparseArray<UserBackupManagerService> getUserServices() {
        return mServiceUsers;
    }

@@ -495,7 +499,8 @@ public class BackupManagerService {

    /**
     * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
     * serial number of the its ancestral work profile.
     * serial number of the its ancestral work profile or null if there is no {@link
     * UserBackupManagerService} associated with that user.
     *
     * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
     * and it corresponds to the profile that was used to restore to the callers profile.
@@ -504,16 +509,18 @@ public class BackupManagerService {
    public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
        int callingUserId = Binder.getCallingUserHandle().getIdentifier();
        long oldId = Binder.clearCallingIdentity();
        int[] userIds;
        final int[] userIds;
        try {
            userIds = mContext.getSystemService(UserManager.class).getProfileIds(callingUserId,
                    false);
            userIds =
                    mContext
                            .getSystemService(UserManager.class)
                            .getProfileIds(callingUserId, false);
        } finally {
            Binder.restoreCallingIdentity(oldId);
        }

        for (int userId : userIds) {
            UserBackupManagerService userBackupManagerService = getServiceUsers().get(userId);
            UserBackupManagerService userBackupManagerService = getUserServices().get(userId);
            if (userBackupManagerService != null) {
                if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) {
                    return UserHandle.of(userId);
@@ -880,28 +887,35 @@ public class BackupManagerService {
    }

    /** Implementation to receive lifecycle event callbacks for system services. */
    public static final class Lifecycle extends SystemService {
    public static class Lifecycle extends SystemService {
        public Lifecycle(Context context) {
            this(context, new Trampoline(context));
        }

        @VisibleForTesting
        Lifecycle(Context context, Trampoline trampoline) {
            super(context);
            sInstance = new Trampoline(context);
            sInstance = trampoline;
        }

        @Override
        public void onStart() {
            publishBinderService(Context.BACKUP_SERVICE, sInstance);
            publishService(Context.BACKUP_SERVICE, sInstance);
        }

        @Override
        public void onUnlockUser(int userId) {
            if (userId == UserHandle.USER_SYSTEM) {
                sInstance.initializeService();
            }
            sInstance.unlockUser(userId);
            sInstance.onUnlockUser(userId);
        }

        @Override
        public void onStopUser(int userId) {
            sInstance.stopUser(userId);
            sInstance.onStopUser(userId);
        }

        @VisibleForTesting
        void publishService(String name, IBinder service) {
            publishBinderService(name, service);
        }
    }
}
+48 −58
Original line number Diff line number Diff line
@@ -40,12 +40,12 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;

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

@@ -73,9 +73,6 @@ import java.io.PrintWriter;
 * Temporary disabling is controlled by {@link #setBackupServiceActive(int, boolean)} through
 * privileged callers (currently {@link DevicePolicyManager}). This is called on {@link
 * UserHandle#USER_SYSTEM} and disables backup for all users.
 *
 * <p>Creation of the backup service is done when {@link UserHandle#USER_SYSTEM} is unlocked. The
 * system user is unlocked before any other users.
 */
public class Trampoline extends IBackupManager.Stub {
    /**
@@ -109,7 +106,10 @@ public class Trampoline extends IBackupManager.Stub {
    // TODD(b/121198006): remove this object and synchronized all methods on "this".
    private final Object mStateLock = new Object();

    private volatile BackupManagerService mService;
    // TODO: This is not marked as final because of test code. Since we'll merge BMS and Trampoline,
    // it doesn't make sense to refactor for final. It's never null.
    @VisibleForTesting
    protected volatile BackupManagerService mService;
    private final Handler mHandler;

    public Trampoline(Context context) {
@@ -120,6 +120,13 @@ public class Trampoline extends IBackupManager.Stub {
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper());
        mUserManager = UserManager.get(context);
        mService = new BackupManagerService(mContext, this);
    }

    // TODO: Remove this when we implement DI by injecting in the construtor.
    @VisibleForTesting
    Handler getBackupHandler() {
        return mHandler;
    }

    protected boolean isBackupDisabled() {
@@ -205,7 +212,7 @@ public class Trampoline extends IBackupManager.Stub {
    // This method should not perform any I/O (e.g. do not call isBackupActivatedForUser),
    // it's used in multiple places where I/O waits would cause system lock-ups.
    private boolean isUserReadyForBackup(int userId) {
        return mService != null && mService.isAbleToServeUser(userId);
        return mService.isAbleToServeUser(userId);
    }

    /**
@@ -230,68 +237,55 @@ public class Trampoline extends IBackupManager.Stub {
        return mUserManager;
    }

    protected BackupManagerService createBackupManagerService() {
        return new BackupManagerService(mContext, this);
    }

    protected void postToHandler(Runnable runnable) {
        mHandler.post(runnable);
    }

    /**
     * Called from {@link BackupManagerService.Lifecycle} when the system user is unlocked. Attempts
     * to initialize {@link BackupManagerService}. Offloads work onto the handler thread {@link
     * #mHandlerThread} to keep unlock time low.
     */
    void initializeService() {
        postToHandler(
                () -> {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
                    if (mGlobalDisable) {
                        Slog.i(TAG, "Backup service not supported");
                        return;
                    }
                    synchronized (mStateLock) {
                        if (mService == null) {
                            mService = createBackupManagerService();
                        }
                    }
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                });
    }

    /**
     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
     * Starts the backup service for this user if backup is active for this user. Offloads work onto
     * the handler thread {@link #mHandlerThread} to keep unlock time low.
     * the handler thread {@link #mHandlerThread} to keep unlock time low since backup is not
     * essential for device functioning.
     */
    void unlockUser(int userId) {
    void onUnlockUser(int userId) {
        postToHandler(() -> startServiceForUser(userId));
    }

    private void startServiceForUser(int userId) {
        // We know that the user is unlocked here because it is called from setBackupServiceActive
        // and unlockUser which have these guarantees. So we can check if the file exists.
        if (mService != null && isBackupActivatedForUser(userId)) {
        if (mGlobalDisable) {
            Slog.i(TAG, "Backup service not supported");
            return;
        }
        if (!isBackupActivatedForUser(userId)) {
            Slog.i(TAG, "Backup not activated for user " + userId);
            return;
        }
        Slog.i(TAG, "Starting service for user: " + userId);
        mService.startServiceForUser(userId);
    }
    }

    /**
     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
     * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
     */
    void stopUser(int userId) {
    void onStopUser(int userId) {
        postToHandler(
                () -> {
                    if (mService != null) {
                    if (!mGlobalDisable) {
                        Slog.i(TAG, "Stopping service for user: " + userId);
                        mService.stopServiceForUser(userId);
                    }
                });
    }

    /** Returns {@link UserBackupManagerService} for user {@code userId}. */
    @Nullable
    public UserBackupManagerService getUserService(int userId) {
        return mService.getUserServices().get(userId);
    }

    /**
     * The system user and managed profiles can only be acted on by callers in the system or root
     * processes. Other users can be acted on by callers who have both android.permission.BACKUP and
@@ -350,9 +344,6 @@ public class Trampoline extends IBackupManager.Stub {
        synchronized (mStateLock) {
            Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
            if (makeActive) {
                if (mService == null) {
                    mService = createBackupManagerService();
                }
                try {
                    activateBackupForUserLocked(userId);
                } catch (IOException e) {
@@ -380,7 +371,7 @@ public class Trampoline extends IBackupManager.Stub {
                }
                //TODO(b/121198006): loop through active users that have work profile and
                // stop them as well.
                stopUser(userId);
                onStopUser(userId);
            }
        }
    }
@@ -388,8 +379,7 @@ public class Trampoline extends IBackupManager.Stub {
    // IBackupManager binder API

    /**
     * Querying activity state of backup service. Calling this method before initialize yields
     * undefined result.
     * Querying activity state of backup service.
     *
     * @param userId The user in which the activity state of backup service is queried.
     * @return true if the service is active.
@@ -397,7 +387,7 @@ public class Trampoline extends IBackupManager.Stub {
    @Override
    public boolean isBackupServiceActive(int userId) {
        synchronized (mStateLock) {
            return mService != null && isBackupActivatedForUser(userId);
            return !mGlobalDisable && isBackupActivatedForUser(userId);
        }
    }

@@ -598,8 +588,8 @@ public class Trampoline extends IBackupManager.Stub {
    @Override
    @Nullable
    public ComponentName getCurrentTransportComponentForUser(int userId) {
        return (isUserReadyForBackup(userId)) ? mService.getCurrentTransportComponent(userId)
                : null;
        return (isUserReadyForBackup(userId))
                ? mService.getCurrentTransportComponent(userId) : null;
    }

    @Override
@@ -614,8 +604,8 @@ public class Trampoline extends IBackupManager.Stub {

    @Override
    public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
        return (isUserReadyForBackup(userId)) ? mService.listAllTransportComponents(userId)
                : null;
        return (isUserReadyForBackup(userId))
                ? mService.listAllTransportComponents(userId) : null;
    }

    @Override
@@ -648,8 +638,8 @@ public class Trampoline extends IBackupManager.Stub {
    @Override
    public String selectBackupTransportForUser(int userId, String transport)
            throws RemoteException {
        return (isUserReadyForBackup(userId)) ? mService.selectBackupTransport(userId, transport)
                : null;
        return (isUserReadyForBackup(userId))
                ? mService.selectBackupTransport(userId, transport) : null;
    }

    @Override
@@ -700,8 +690,8 @@ public class Trampoline extends IBackupManager.Stub {
    @Override
    public Intent getDataManagementIntentForUser(int userId, String transport)
            throws RemoteException {
        return isUserReadyForBackup(userId) ? mService.getDataManagementIntent(userId, transport)
                : null;
        return isUserReadyForBackup(userId)
                ? mService.getDataManagementIntent(userId, transport) : null;
    }

    @Override
@@ -784,15 +774,15 @@ public class Trampoline extends IBackupManager.Stub {

    @Override
    @Nullable public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
        if (mService != null) {
            return mService.getUserForAncestralSerialNumber(ancestralSerialNumber);
        }
        if (mGlobalDisable) {
            return null;
        }
        return mService.getUserForAncestralSerialNumber(ancestralSerialNumber);
    }

    @Override
    public void setAncestralSerialNumber(long ancestralSerialNumber) {
        if (mService != null) {
        if (!mGlobalDisable) {
            mService.setAncestralSerialNumber(ancestralSerialNumber);
        }
    }
+51 −5
Original line number Diff line number Diff line
@@ -26,8 +26,12 @@ import static com.android.server.backup.testing.TransportData.backupTransport;
import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
import static org.testng.Assert.expectThrows;
@@ -121,7 +125,7 @@ public class BackupManagerServiceTest {
    public void testConstructor_doesNotRegisterUsers() throws Exception {
        BackupManagerService backupManagerService = createService();

        assertThat(backupManagerService.getServiceUsers().size()).isEqualTo(0);
        assertThat(backupManagerService.getUserServices().size()).isEqualTo(0);
    }

    /** Test that the constructor handles {@code null} parameters. */
@@ -152,7 +156,7 @@ public class BackupManagerServiceTest {

        backupManagerService.startServiceForUser(mUserOneId);

        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
        assertThat(serviceUsers.size()).isEqualTo(1);
        assertThat(serviceUsers.get(mUserOneId)).isNotNull();
    }
@@ -164,7 +168,7 @@ public class BackupManagerServiceTest {

        backupManagerService.startServiceForUser(mUserOneId, mUserOneService);

        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
        assertThat(serviceUsers.size()).isEqualTo(1);
        assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
    }
@@ -178,7 +182,7 @@ public class BackupManagerServiceTest {

        backupManagerService.stopServiceForUser(mUserOneId);

        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
        assertThat(serviceUsers.size()).isEqualTo(1);
        assertThat(serviceUsers.get(mUserOneId)).isNull();
        assertThat(serviceUsers.get(mUserTwoId)).isEqualTo(mUserTwoService);
@@ -204,7 +208,7 @@ public class BackupManagerServiceTest {

        backupManagerService.stopServiceForUser(mUserOneId);

        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
        assertThat(serviceUsers.size()).isEqualTo(0);
    }

@@ -1499,6 +1503,48 @@ public class BackupManagerServiceTest {
                        observer);
    }

    // ---------------------------------------------
    //  Lifecycle tests
    // ---------------------------------------------


    /** testOnStart_publishesService */
    @Test
    public void testOnStart_publishesService() {
        Trampoline trampoline = mock(Trampoline.class);
        BackupManagerService.Lifecycle lifecycle =
                spy(new BackupManagerService.Lifecycle(mContext, trampoline));
        doNothing().when(lifecycle).publishService(anyString(), any());

        lifecycle.onStart();

        verify(lifecycle).publishService(Context.BACKUP_SERVICE, trampoline);
    }

    /** testOnUnlockUser_forwards */
    @Test
    public void testOnUnlockUser_forwards() {
        Trampoline trampoline = mock(Trampoline.class);
        BackupManagerService.Lifecycle lifecycle =
                new BackupManagerService.Lifecycle(mContext, trampoline);

        lifecycle.onUnlockUser(UserHandle.USER_SYSTEM);

        verify(trampoline).onUnlockUser(UserHandle.USER_SYSTEM);
    }

    /** testOnStopUser_forwards */
    @Test
    public void testOnStopUser_forwards() {
        Trampoline trampoline = mock(Trampoline.class);
        BackupManagerService.Lifecycle lifecycle =
                new BackupManagerService.Lifecycle(mContext, trampoline);

        lifecycle.onStopUser(UserHandle.USER_SYSTEM);

        verify(trampoline).onStopUser(UserHandle.USER_SYSTEM);
    }

    // ---------------------------------------------
    //  Service tests
    // ---------------------------------------------
+92 −369

File changed.

Preview size limit exceeded, changes collapsed.