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

Commit 9b55bfbd authored by Bernardo Rufino's avatar Bernardo Rufino
Browse files

Move start and stop of sub-systems to Trampoline

From BMS. Moved mUserServices list creation from BMS to Trampoline.
BMS still uses it but the plan is to move gradually in multiple
CLs to eventually delete it. Both are dealing with the same instance
because of this. Start and stop of sub-system (uBMS) is now handled in
Trampoline. getUserServices() was also moved. BMS robolectric tests were moved
to Trampoline *robolectric* tests to simplify this CL. After we merge BMS and
Trampoline, we can look into moving tests away from Robolectric.

Test: atest BackupManagerServiceTest TrampolineRoboTest TrampolineTest
Bug: 135661048
Change-Id: I4e612d06fe006c12f215a94f210450a5e6316b75
parent ac81c6a0
Loading
Loading
Loading
Loading
+8 −49
Original line number Diff line number Diff line
@@ -38,7 +38,6 @@ import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
@@ -50,7 +49,6 @@ import com.android.server.SystemService;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Set;

/**
 * Definition of the system service that performs backup/restore operations.
@@ -70,14 +68,17 @@ public class BackupManagerService {

    private final Context mContext;
    private final Trampoline mTrampoline;

    // Keeps track of all unlocked users registered with this service. Indexed by user id.
    private final SparseArray<UserBackupManagerService> mServiceUsers = new SparseArray<>();
    private final SparseArray<UserBackupManagerService> mServiceUsers;

    /** Instantiate a new instance of {@link BackupManagerService}. */
    public BackupManagerService(Context context, Trampoline trampoline) {
    public BackupManagerService(
            Context context,
            Trampoline trampoline,
            SparseArray<UserBackupManagerService> userServices) {
        mContext = checkNotNull(context);
        mTrampoline = checkNotNull(trampoline);
        // TODO(b/135661048): Remove
        mServiceUsers = userServices;
    }

    /**
@@ -98,48 +99,6 @@ public class BackupManagerService {
    // USER LIFECYCLE CALLBACKS
    // ---------------------------------------------

    /**
     * Starts the backup service for user {@code userId} by creating a new instance of {@link
     * UserBackupManagerService} and registering it with this service.
     */
    @VisibleForTesting
    protected void startServiceForUser(int userId, Set<ComponentName> transportWhitelist) {
        if (mServiceUsers.get(userId) != null) {
            Slog.i(TAG, "userId " + userId + " already started, so not starting again");
            return;
        }

        UserBackupManagerService userBackupManagerService =
                UserBackupManagerService.createAndInitializeService(
                        userId, mContext, mTrampoline, transportWhitelist);
        startServiceForUser(userId, userBackupManagerService);
    }

    /**
     * Starts the backup service for user {@code userId} by registering its instance of {@link
     * UserBackupManagerService} with this service and setting enabled state.
     */
    void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
        mServiceUsers.put(userId, userBackupManagerService);

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
        userBackupManagerService.initializeBackupEnableState();
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }

    /** Stops the backup service for user {@code userId} when the user is stopped. */
    @VisibleForTesting
    protected void stopServiceForUser(int userId) {
        UserBackupManagerService userBackupManagerService = mServiceUsers.removeReturnOld(userId);

        if (userBackupManagerService != null) {
            userBackupManagerService.tearDownService();

            KeyValueBackupJob.cancel(userId, mContext);
            FullBackupJob.cancel(userId, mContext);
        }
    }

    boolean isAbleToServeUser(int userId) {
        return getUserServices().get(UserHandle.USER_SYSTEM) != null
                && getUserServices().get(userId) != null;
@@ -453,7 +412,7 @@ public class BackupManagerService {
        }

        for (int userId : userIds) {
            UserBackupManagerService userBackupManagerService = getUserServices().get(userId);
            UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId);
            if (userBackupManagerService != null) {
                if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) {
                    return UserHandle.of(userId);
+66 −5
Original line number Diff line number Diff line
@@ -46,9 +46,11 @@ 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 android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -127,6 +129,9 @@ public class Trampoline extends IBackupManager.Stub {
    private final Handler mHandler;
    private final Set<ComponentName> mTransportWhitelist;

    /** Keeps track of all unlocked users registered with this service. Indexed by user id. */
    private final SparseArray<UserBackupManagerService> mUserServices;

    private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
@@ -140,6 +145,11 @@ public class Trampoline extends IBackupManager.Stub {
    };

    public Trampoline(Context context) {
        this(context, new SparseArray<>());
    }

    @VisibleForTesting
    Trampoline(Context context, SparseArray<UserBackupManagerService> userServices) {
        mContext = context;
        mGlobalDisable = isBackupDisabled();
        HandlerThread handlerThread =
@@ -147,7 +157,8 @@ public class Trampoline extends IBackupManager.Stub {
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper());
        mUserManager = UserManager.get(context);
        mService = new BackupManagerService(mContext, this);
        mUserServices = userServices;
        mService = new BackupManagerService(mContext, this, mUserServices);
        Set<ComponentName> transportWhitelist =
                SystemConfig.getInstance().getBackupTransportWhitelist();
        mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist;
@@ -297,7 +308,12 @@ public class Trampoline extends IBackupManager.Stub {
        postToHandler(() -> startServiceForUser(userId));
    }

    private void startServiceForUser(int userId) {
    /**
     * Starts the backup service for user {@code userId} by creating a new instance of {@link
     * UserBackupManagerService} and registering it with this service.
     */
    @VisibleForTesting
    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 (mGlobalDisable) {
@@ -308,8 +324,53 @@ public class Trampoline extends IBackupManager.Stub {
            Slog.i(TAG, "Backup not activated for user " + userId);
            return;
        }
        if (mUserServices.get(userId) != null) {
            Slog.i(TAG, "userId " + userId + " already started, so not starting again");
            return;
        }
        Slog.i(TAG, "Starting service for user: " + userId);
        mService.startServiceForUser(userId, mTransportWhitelist);
        UserBackupManagerService userBackupManagerService =
                UserBackupManagerService.createAndInitializeService(
                        userId, mContext, this, mTransportWhitelist);
        startServiceForUser(userId, userBackupManagerService);
    }

    /**
     * Starts the backup service for user {@code userId} by registering its instance of {@link
     * UserBackupManagerService} with this service and setting enabled state.
     */
    void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
        mUserServices.put(userId, userBackupManagerService);

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
        userBackupManagerService.initializeBackupEnableState();
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }

    /** Stops the backup service for user {@code userId} when the user is stopped. */
    @VisibleForTesting
    protected void stopServiceForUser(int userId) {
        UserBackupManagerService userBackupManagerService = mUserServices.removeReturnOld(userId);

        if (userBackupManagerService != null) {
            userBackupManagerService.tearDownService();

            KeyValueBackupJob.cancel(userId, mContext);
            FullBackupJob.cancel(userId, mContext);
        }
    }

    /**
     *  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
    SparseArray<UserBackupManagerService> getUserServices() {
        return mUserServices;
    }

    /**
@@ -321,7 +382,7 @@ public class Trampoline extends IBackupManager.Stub {
                () -> {
                    if (!mGlobalDisable) {
                        Slog.i(TAG, "Stopping service for user: " + userId);
                        mService.stopServiceForUser(userId);
                        stopServiceForUser(userId);
                    }
                });
    }
@@ -329,7 +390,7 @@ public class Trampoline extends IBackupManager.Stub {
    /** Returns {@link UserBackupManagerService} for user {@code userId}. */
    @Nullable
    public UserBackupManagerService getUserService(int userId) {
        return mService.getUserServices().get(userId);
        return mUserServices.get(userId);
    }

    /**
+2 −1
Original line number Diff line number Diff line
@@ -649,7 +649,8 @@ public class UserBackupManagerService {
    }

    /** Cleans up state when the user of this service is stopped. */
    void tearDownService() {
    @VisibleForTesting
    protected void tearDownService() {
        mAgentTimeoutParameters.stop();
        mConstants.stop();
        mContext.getContentResolver().unregisterContentObserver(mSetupObserver);
+208 −252

File changed.

Preview size limit exceeded, changes collapsed.

+175 −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.backup;

import static android.Manifest.permission.BACKUP;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;

import android.annotation.UserIdInt;
import android.app.Application;
import android.content.Context;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;

import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowBinder;
import com.android.server.testing.shadows.ShadowEnvironment;
import com.android.server.testing.shadows.ShadowSystemServiceRegistry;
import com.android.server.testing.shadows.ShadowUserManager;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowContextWrapper;

/** Tests for {@link com.android.server.backup.Trampoline}. */
@RunWith(RobolectricTestRunner.class)
@Config(
        shadows = {
                ShadowApplicationPackageManager.class,
                ShadowBinder.class,
                ShadowUserManager.class,
                ShadowEnvironment.class,
                ShadowSystemServiceRegistry.class
        })
@Presubmit
public class TrampolineRoboTest {
    private Context mContext;
    private ShadowContextWrapper mShadowContext;
    private ShadowUserManager mShadowUserManager;
    @UserIdInt private int mUserOneId;
    @UserIdInt private int mUserTwoId;
    @Mock private UserBackupManagerService mUserOneService;
    @Mock private UserBackupManagerService mUserTwoService;

    /** Setup */
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        Application application = RuntimeEnvironment.application;
        mContext = application;
        mShadowContext = shadowOf(application);
        mShadowUserManager = Shadow.extract(UserManager.get(application));

        mUserOneId = UserHandle.USER_SYSTEM + 1;
        mUserTwoId = mUserOneId + 1;
        mShadowUserManager.addUser(mUserOneId, "mUserOneId", 0);
        mShadowUserManager.addUser(mUserTwoId, "mUserTwoId", 0);

        mShadowContext.grantPermissions(BACKUP);
        mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);

        ShadowBinder.setCallingUid(Process.SYSTEM_UID);
    }

    /** Test that the service registers users. */
    @Test
    public void testStartServiceForUser_registersUser() throws Exception {
        Trampoline backupManagerService = createService();
        backupManagerService.setBackupServiceActive(mUserOneId, true);

        backupManagerService.startServiceForUser(mUserOneId);

        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
        assertThat(serviceUsers.size()).isEqualTo(1);
        assertThat(serviceUsers.get(mUserOneId)).isNotNull();
    }

    /** Test that the service registers users. */
    @Test
    public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception {
        Trampoline backupManagerService = createService();
        backupManagerService.setBackupServiceActive(mUserOneId, true);

        backupManagerService.startServiceForUser(mUserOneId, mUserOneService);

        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
        assertThat(serviceUsers.size()).isEqualTo(1);
        assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
    }

    /** Test that the service unregisters users when stopped. */
    @Test
    public void testStopServiceForUser_forRegisteredUser_unregistersCorrectUser() throws Exception {
        Trampoline backupManagerService =
                createServiceAndRegisterUser(mUserOneId, mUserOneService);
        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
        ShadowBinder.setCallingUid(Process.SYSTEM_UID);

        backupManagerService.stopServiceForUser(mUserOneId);

        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
        assertThat(serviceUsers.size()).isEqualTo(1);
        assertThat(serviceUsers.get(mUserOneId)).isNull();
        assertThat(serviceUsers.get(mUserTwoId)).isEqualTo(mUserTwoService);
    }

    /** Test that the service unregisters users when stopped. */
    @Test
    public void testStopServiceForUser_forRegisteredUser_tearsDownCorrectUser() throws Exception {
        Trampoline backupManagerService =
                createServiceAndRegisterUser(mUserOneId, mUserOneService);
        backupManagerService.setBackupServiceActive(mUserTwoId, true);
        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);

        backupManagerService.stopServiceForUser(mUserOneId);

        verify(mUserOneService).tearDownService();
        verify(mUserTwoService, never()).tearDownService();
    }

    /** Test that the service unregisters users when stopped. */
    @Test
    public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception {
        Trampoline backupManagerService = createService();
        backupManagerService.setBackupServiceActive(mUserOneId, true);
        ShadowBinder.setCallingUid(Process.SYSTEM_UID);

        backupManagerService.stopServiceForUser(mUserOneId);

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

    private Trampoline createService() {
        return new Trampoline(mContext);
    }

    private Trampoline createServiceAndRegisterUser(
            int userId, UserBackupManagerService userBackupManagerService) {
        Trampoline backupManagerService = createService();
        backupManagerService.setBackupServiceActive(userBackupManagerService.getUserId(), true);
        backupManagerService.startServiceForUser(userId, userBackupManagerService);
        return backupManagerService;
    }
}
Loading