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

Commit 47f61fc9 authored by Ruslan Tkhakokhov's avatar Ruslan Tkhakokhov Committed by Android (Google) Code Review
Browse files

Merge changes from topic "fsd2d-backup-agent"

* changes:
  [FSD2D] Pass @OperationType at agent creation in B&R code
  [FSD2D] Adjust backup agent creation for device-to-device migrations
parents 6b4cf956 9cc61d58
Loading
Loading
Loading
Loading
+25 −8
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.annotation.Nullable;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.backup.BackupAgent;
import android.app.backup.BackupManager;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
import android.app.servertransaction.ActivityRelaunchItem;
@@ -289,6 +290,8 @@ public final class ActivityThread extends ClientTransactionHandler {

    private final Object mNetworkPolicyLock = new Object();

    private static final String DEFAULT_FULL_BACKUP_AGENT = "android.app.backup.FullBackupAgent";

    /**
     * Denotes the sequence number of the process state change for which the main thread needs
     * to block until the network rules are updated for it.
@@ -737,6 +740,7 @@ public final class ActivityThread extends ClientTransactionHandler {
        CompatibilityInfo compatInfo;
        int backupMode;
        int userId;
        int operationType;
        public String toString() {
            return "CreateBackupAgentData{appInfo=" + appInfo
                    + " backupAgent=" + appInfo.backupAgentName
@@ -957,12 +961,13 @@ public final class ActivityThread extends ClientTransactionHandler {
        }

        public final void scheduleCreateBackupAgent(ApplicationInfo app,
                CompatibilityInfo compatInfo, int backupMode, int userId) {
                CompatibilityInfo compatInfo, int backupMode, int userId, int operationType) {
            CreateBackupAgentData d = new CreateBackupAgentData();
            d.appInfo = app;
            d.compatInfo = compatInfo;
            d.backupMode = backupMode;
            d.userId = userId;
            d.operationType = operationType;

            sendMessage(H.CREATE_BACKUP_AGENT, d);
        }
@@ -4075,12 +4080,7 @@ public final class ActivityThread extends ClientTransactionHandler {
            return;
        }

        String classname = data.appInfo.backupAgentName;
        // full backup operation but no app-supplied agent?  use the default implementation
        if (classname == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
                || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
            classname = "android.app.backup.FullBackupAgent";
        }
        String classname = getBackupAgentName(data);

        try {
            IBinder binder = null;
@@ -4104,7 +4104,7 @@ public final class ActivityThread extends ClientTransactionHandler {
                    context.setOuterContext(agent);
                    agent.attach(context);

                    agent.onCreate(UserHandle.of(data.userId));
                    agent.onCreate(UserHandle.of(data.userId), data.operationType);
                    binder = agent.onBind();
                    backupAgents.put(packageName, agent);
                } catch (Exception e) {
@@ -4132,6 +4132,23 @@ public final class ActivityThread extends ClientTransactionHandler {
        }
    }

    private String getBackupAgentName(CreateBackupAgentData data) {
        String agentName = data.appInfo.backupAgentName;
        if (!UserHandle.isCore(data.appInfo.uid)
                && data.operationType == BackupManager.OperationType.MIGRATION) {
            // If this is a migration, use the default backup agent regardless of the app's
            // preferences.
            agentName = DEFAULT_FULL_BACKUP_AGENT;
        } else {
            // full backup operation but no app-supplied agent?  use the default implementation
            if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
                    || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
                agentName = DEFAULT_FULL_BACKUP_AGENT;
            }
        }
        return agentName;
    }

    // Tear down a BackupAgent
    private void handleDestroyBackupAgent(CreateBackupAgentData data) {
        if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
+2 −1
Original line number Diff line number Diff line
@@ -288,7 +288,8 @@ interface IActivityManager {
    void stopAppSwitches();
    @UnsupportedAppUsage
    void resumeAppSwitches();
    boolean bindBackupAgent(in String packageName, int backupRestoreMode, int targetUserId);
    boolean bindBackupAgent(in String packageName, int backupRestoreMode, int targetUserId,
            int operationType);
    void backupAgentCreated(in String packageName, in IBinder agent, int userId);
    void unbindBackupAgent(in ApplicationInfo appInfo);
    int getUidForIntentSender(in IIntentSender sender);
+1 −1
Original line number Diff line number Diff line
@@ -95,7 +95,7 @@ oneway interface IApplicationThread {
    void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType);
    void setSchedulingGroup(int group);
    void scheduleCreateBackupAgent(in ApplicationInfo app, in CompatibilityInfo compatInfo,
            int backupMode, int userId);
            int backupMode, int userId, int operationType);
    void scheduleDestroyBackupAgent(in ApplicationInfo app,
            in CompatibilityInfo compatInfo, int userId);
    void scheduleOnNewActivityOptions(IBinder token, in Bundle options);
+93 −9
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.app.backup;
import android.annotation.Nullable;
import android.app.IBackupAgent;
import android.app.QueuedWork;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
import android.content.Context;
import android.content.ContextWrapper;
@@ -38,6 +39,8 @@ import android.system.StructStat;
import android.util.ArraySet;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;

import libcore.io.IoUtils;

import org.xmlpull.v1.XmlPullParserException;
@@ -50,6 +53,7 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;

@@ -129,6 +133,7 @@ import java.util.concurrent.CountDownLatch;
public abstract class BackupAgent extends ContextWrapper {
    private static final String TAG = "BackupAgent";
    private static final boolean DEBUG = false;
    private static final int DEFAULT_OPERATION_TYPE = OperationType.BACKUP;

    /** @hide */
    public static final int RESULT_SUCCESS = 0;
@@ -186,6 +191,9 @@ public abstract class BackupAgent extends ContextWrapper {
    Handler mHandler = null;

    @Nullable private UserHandle mUser;
     // This field is written from the main thread (in onCreate), and read in a Binder thread (in
     // onFullBackup that is called from system_server via Binder).
    @OperationType private volatile int mOperationType = DEFAULT_OPERATION_TYPE;

    Handler getHandler() {
        if (mHandler == null) {
@@ -228,6 +236,13 @@ public abstract class BackupAgent extends ContextWrapper {
    public void onCreate() {
    }

    /**
     * @hide
     */
    public void onCreate(UserHandle user) {
        onCreate(user, DEFAULT_OPERATION_TYPE);
    }

    /**
     * Provided as a convenience for agent implementations that need an opportunity
     * to do one-time initialization before the actual backup or restore operation
@@ -236,10 +251,11 @@ public abstract class BackupAgent extends ContextWrapper {
     *
     * @hide
     */
    public void onCreate(UserHandle user) {
    public void onCreate(UserHandle user, @OperationType int operationType) {
        onCreate();

        mUser = user;
        mOperationType = operationType;
    }

    /**
@@ -390,12 +406,9 @@ public abstract class BackupAgent extends ContextWrapper {
            return;
        }

        Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap;
        ArraySet<PathWithRequiredFlags> manifestExcludeSet;
        IncludeExcludeRules includeExcludeRules;
        try {
            manifestIncludeMap =
                    backupScheme.maybeParseAndGetCanonicalIncludePaths();
            manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths();
            includeExcludeRules = getIncludeExcludeRules(backupScheme);
        } catch (IOException | XmlPullParserException e) {
            if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
                Log.v(FullBackup.TAG_XML_PARSER,
@@ -404,6 +417,10 @@ public abstract class BackupAgent extends ContextWrapper {
            }
            return;
        }
        Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap
                = includeExcludeRules.getIncludeMap();
        Set<PathWithRequiredFlags> manifestExcludeSet
                = includeExcludeRules.getExcludeSet();

        final String packageName = getPackageName();
        final ApplicationInfo appInfo = getApplicationInfo();
@@ -528,6 +545,24 @@ public abstract class BackupAgent extends ContextWrapper {
        }
    }

    /** @hide */
    @VisibleForTesting
    public IncludeExcludeRules getIncludeExcludeRules(FullBackup.BackupScheme backupScheme)
            throws IOException, XmlPullParserException {
        if (mOperationType == OperationType.MIGRATION) {
            return IncludeExcludeRules.emptyRules();
        }

        Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap;
        ArraySet<PathWithRequiredFlags> manifestExcludeSet;

        manifestIncludeMap =
                backupScheme.maybeParseAndGetCanonicalIncludePaths();
        manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths();

        return new IncludeExcludeRules(manifestIncludeMap, manifestExcludeSet);
    }

    /**
     * Notification that the application's current backup operation causes it to exceed
     * the maximum size permitted by the transport.  The ongoing backup operation is
@@ -570,7 +605,7 @@ public abstract class BackupAgent extends ContextWrapper {
     */
    private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken,
            Map<String, Set<PathWithRequiredFlags>> includeMap,
            ArraySet<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet,
            Set<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet,
            FullBackupDataOutput data) throws IOException {
        if (includeMap == null || includeMap.size() == 0) {
            // Do entire sub-tree for the provided token.
@@ -742,7 +777,7 @@ public abstract class BackupAgent extends ContextWrapper {
     * @hide
     */
    protected final void fullBackupFileTree(String packageName, String domain, String startingPath,
                                            ArraySet<PathWithRequiredFlags> manifestExcludes,
                                            Set<PathWithRequiredFlags> manifestExcludes,
                                            ArraySet<String> systemExcludes,
            FullBackupDataOutput output) {
        // Pull out the domain and set it aside to use when making the tarball.
@@ -811,7 +846,7 @@ public abstract class BackupAgent extends ContextWrapper {
    }

    private boolean manifestExcludesContainFilePath(
        ArraySet<PathWithRequiredFlags> manifestExcludes, String filePath) {
        Set<PathWithRequiredFlags> manifestExcludes, String filePath) {
        for (PathWithRequiredFlags exclude : manifestExcludes) {
            String excludePath = exclude.getPath();
            if (excludePath != null && excludePath.equals(filePath)) {
@@ -1265,4 +1300,53 @@ public abstract class BackupAgent extends ContextWrapper {
            throw new IllegalStateException(mMessage);
        }
    }

    /**  @hide */
    @VisibleForTesting
    public static class IncludeExcludeRules {
        private final Map<String, Set<PathWithRequiredFlags>> mManifestIncludeMap;
        private final Set<PathWithRequiredFlags> mManifestExcludeSet;

        /** @hide */
        public IncludeExcludeRules(
                Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap,
                Set<PathWithRequiredFlags> manifestExcludeSet) {
            mManifestIncludeMap = manifestIncludeMap;
            mManifestExcludeSet = manifestExcludeSet;
        }

        /**  @hide */
        @VisibleForTesting
        public static IncludeExcludeRules emptyRules() {
            return new IncludeExcludeRules(Collections.emptyMap(), new ArraySet<>());
        }

        private Map<String, Set<PathWithRequiredFlags>> getIncludeMap() {
            return mManifestIncludeMap;
        }

        private Set<PathWithRequiredFlags> getExcludeSet() {
            return mManifestExcludeSet;
        }

        /**  @hide */
        @Override
        public int hashCode() {
            return Objects.hash(mManifestIncludeMap, mManifestExcludeSet);
        }

        /**  @hide */
        @Override
        public boolean equals(Object object) {
            if (this == object) {
                return true;
            }
            if (object == null || getClass() != object.getClass()) {
                return false;
            }
            IncludeExcludeRules that = (IncludeExcludeRules) object;
            return Objects.equals(mManifestIncludeMap, that.mManifestIncludeMap) &&
                    Objects.equals(mManifestExcludeSet, that.mManifestExcludeSet);
        }
    }
}
+104 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.app.backup;

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

import static org.mockito.Mockito.when;

import android.app.backup.BackupAgent.IncludeExcludeRules;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;

import androidx.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

@Presubmit
@RunWith(AndroidJUnit4.class)
public class BackupAgentTest {
    // An arbitrary user.
    private static final UserHandle USER_HANDLE = new UserHandle(15);

    @Mock FullBackup.BackupScheme mBackupScheme;

    private BackupAgent mBackupAgent;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testGetIncludeExcludeRules_isMigration_returnsEmptyRules()  throws Exception {
        mBackupAgent = getAgentForOperationType(OperationType.MIGRATION);

        IncludeExcludeRules rules = mBackupAgent.getIncludeExcludeRules(mBackupScheme);
        assertThat(rules).isEqualTo(IncludeExcludeRules.emptyRules());
    }

    @Test
    public void testGetIncludeExcludeRules_isNotMigration_returnsRules() throws Exception {
        PathWithRequiredFlags path = new PathWithRequiredFlags("path", /* requiredFlags */ 0);
        Map<String, Set<PathWithRequiredFlags>> includePaths = Collections.singletonMap("test",
                Collections.singleton(path));
        ArraySet<PathWithRequiredFlags> excludePaths = new ArraySet<>();
        excludePaths.add(path);
        IncludeExcludeRules expectedRules = new IncludeExcludeRules(includePaths, excludePaths);

        mBackupAgent = getAgentForOperationType(OperationType.BACKUP);
        when(mBackupScheme.maybeParseAndGetCanonicalExcludePaths()).thenReturn(excludePaths);
        when(mBackupScheme.maybeParseAndGetCanonicalIncludePaths()).thenReturn(includePaths);

        IncludeExcludeRules rules = mBackupAgent.getIncludeExcludeRules(mBackupScheme);
        assertThat(rules).isEqualTo(expectedRules);
    }

    private BackupAgent getAgentForOperationType(@OperationType int operationType) {
        BackupAgent agent = new TestFullBackupAgent();
        agent.onCreate(USER_HANDLE, operationType);
        return agent;
    }

    private static class TestFullBackupAgent extends BackupAgent {

        @Override
        public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
                ParcelFileDescriptor newState) throws IOException {
            // Left empty as this is a full backup agent.
        }

        @Override
        public void onRestore(BackupDataInput data, int appVersionCode,
                ParcelFileDescriptor newState) throws IOException {
            // Left empty as this is a full backup agent.
        }
    }
}
Loading