Merge "Update lint_fix to remove stale suggested-fixes.zip"
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 4bba338..6a323d6 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -123,17 +123,30 @@
 }
 
 droidstubs {
-    name: "framework-doc-system-stubs",
-    defaults: ["framework-doc-stubs-sources-default"],
-    args: metalava_framework_docs_args +
-        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
-    api_levels_annotations_enabled: true,
-    api_levels_annotations_dirs: [
-        "sdk-dir",
-        "api-versions-jars-dir",
+    name: "android-non-updatable-doc-stubs-module-lib",
+    defaults: [
+        "android-non-updatable-doc-stubs-defaults",
+        "module-classpath-stubs-defaults",
     ],
-    api_levels_sdk_type: "system",
-    extensions_info_file: ":sdk-extensions-info",
+    args: metalava_framework_docs_args +
+        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) " +
+        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\) ",
+    generate_stubs: false, // We're only using this module for the annotations.zip output, disable doc-stubs.
+    write_sdk_values: false,
+}
+
+droidstubs {
+    name: "android-non-updatable-doc-stubs-system-server",
+    defaults: [
+        "android-non-updatable-doc-stubs-defaults",
+        "module-classpath-stubs-defaults",
+    ],
+    args: metalava_framework_docs_args +
+        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) " +
+        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\) " +
+        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\) ",
+    generate_stubs: false, // We're only using this module for the annotations.zip output, disable doc-stubs.
+    write_sdk_values: false,
 }
 
 droidstubs {
@@ -151,6 +164,20 @@
     extensions_info_file: ":sdk-extensions-info",
 }
 
+droidstubs {
+    name: "framework-doc-system-stubs",
+    defaults: ["framework-doc-stubs-sources-default"],
+    args: metalava_framework_docs_args +
+        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
+    ],
+    api_levels_sdk_type: "system",
+    extensions_info_file: ":sdk-extensions-info",
+}
+
 /////////////////////////////////////////////////////////////////////
 // API docs are created from the generated stub source files
 // using droiddoc
diff --git a/StubLibraries.bp b/StubLibraries.bp
index bc2e6dd..0e08496 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -411,6 +411,36 @@
     ],
 }
 
+java_library {
+    name: "android_module_stubs_current_with_test_libs",
+    static_libs: [
+        "android_module_lib_stubs_current",
+        "android.test.base.stubs",
+        "android.test.mock.stubs",
+        "android.test.runner.stubs",
+    ],
+    defaults: ["android.jar_defaults"],
+    visibility: [
+        "//visibility:override",
+        "//visibility:private",
+    ],
+}
+
+java_library {
+    name: "android_system_server_stubs_current_with_test_libs",
+    static_libs: [
+        "android_system_server_stubs_current",
+        "android.test.base.stubs.system",
+        "android.test.mock.stubs.system",
+        "android.test.runner.stubs.system",
+    ],
+    defaults: ["android.jar_defaults"],
+    visibility: [
+        "//visibility:override",
+        "//visibility:private",
+    ],
+}
+
 droidstubs {
     name: "api_versions_public",
     srcs: [":android_stubs_current_with_test_libs{.jar}"],
@@ -420,6 +450,7 @@
         "sdk-dir",
         "api-versions-jars-dir",
     ],
+    api_levels_sdk_type: "public",
     extensions_info_file: ":sdk-extensions-info",
 }
 
@@ -436,6 +467,32 @@
     extensions_info_file: ":sdk-extensions-info",
 }
 
+droidstubs {
+    name: "api_versions_module_lib",
+    srcs: [":android_module_stubs_current_with_test_libs{.jar}"],
+    generate_stubs: false,
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
+    ],
+    api_levels_sdk_type: "module-lib",
+    extensions_info_file: ":sdk-extensions-info",
+}
+
+droidstubs {
+    name: "api_versions_system_server",
+    srcs: [":android_system_server_stubs_current_with_test_libs{.jar}"],
+    generate_stubs: false,
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
+    ],
+    api_levels_sdk_type: "system-server",
+    extensions_info_file: ":sdk-extensions-info",
+}
+
 /////////////////////////////////////////////////////////////////////
 // hwbinder.stubs provides APIs required for building HIDL Java
 // libraries.
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 901796b..bedfa7f 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -87,6 +87,7 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.BatteryManager;
+import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -116,6 +117,7 @@
 import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.IndentingPrintWriter;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.LongArrayQueue;
 import android.util.Pair;
@@ -249,6 +251,7 @@
     private ActivityManagerInternal mActivityManagerInternal;
     private final EconomyManagerInternal mEconomyManagerInternal;
     private PackageManagerInternal mPackageManagerInternal;
+    private BatteryStatsInternal mBatteryStatsInternal;
     private RoleManager mRoleManager;
     private volatile PermissionManagerServiceInternal mLocalPermissionManager;
 
@@ -2113,6 +2116,8 @@
                     LocalServices.getService(AppStandbyInternal.class);
             appStandbyInternal.addListener(new AppStandbyTracker());
 
+            mBatteryStatsInternal = LocalServices.getService(BatteryStatsInternal.class);
+
             mRoleManager = getContext().getSystemService(RoleManager.class);
 
             mMetricsHelper.registerPuller(() -> mAlarmStore);
@@ -4783,8 +4788,12 @@
                             }
                             final ArraySet<Pair<String, Integer>> triggerPackages =
                                     new ArraySet<>();
+                            final IntArray wakeupUids = new IntArray();
                             for (int i = 0; i < triggerList.size(); i++) {
                                 final Alarm a = triggerList.get(i);
+                                if (a.wakeup) {
+                                    wakeupUids.add(a.uid);
+                                }
                                 if (mConstants.USE_TARE_POLICY) {
                                     if (!isExemptFromTare(a)) {
                                         triggerPackages.add(Pair.create(
@@ -4796,6 +4805,11 @@
                                             a.sourcePackage, UserHandle.getUserId(a.creatorUid)));
                                 }
                             }
+                            if (wakeupUids.size() > 0 && mBatteryStatsInternal != null) {
+                                mBatteryStatsInternal.noteCpuWakingActivity(
+                                        BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM, nowELAPSED,
+                                        wakeupUids.toArray());
+                            }
                             deliverAlarmsLocked(triggerList, nowELAPSED);
                             mTemporaryQuotaReserve.cleanUpExpiredQuotas(nowELAPSED);
                             if (mConstants.USE_TARE_POLICY) {
diff --git a/api/Android.bp b/api/Android.bp
index 07fd850..9306671 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -213,6 +213,24 @@
 }
 
 genrule {
+    name: "sdk-annotations-module-lib.zip",
+    defaults: ["sdk-annotations-defaults"],
+    srcs: [
+        ":android-non-updatable-doc-stubs-module-lib{.annotations.zip}",
+        ":all-modules-module-lib-annotations",
+    ],
+}
+
+genrule {
+    name: "sdk-annotations-system-server.zip",
+    defaults: ["sdk-annotations-defaults"],
+    srcs: [
+        ":android-non-updatable-doc-stubs-system-server{.annotations.zip}",
+        ":all-modules-system-server-annotations",
+    ],
+}
+
+genrule {
     name: "combined-removed-dex",
     visibility: [
         "//frameworks/base/boot",
diff --git a/api/api.go b/api/api.go
index f158041..6a6c493 100644
--- a/api/api.go
+++ b/api/api.go
@@ -148,18 +148,35 @@
 	ctx.CreateModule(genrule.GenRuleFactory, &props)
 }
 
-func createMergedPublicAnnotationsFilegroup(ctx android.LoadHookContext, modules []string) {
-	props := fgProps{}
-	props.Name = proptools.StringPtr("all-modules-public-annotations")
-	props.Srcs = createSrcs(modules, "{.public.annotations.zip}")
-	ctx.CreateModule(android.FileGroupFactory, &props)
-}
-
-func createMergedSystemAnnotationsFilegroup(ctx android.LoadHookContext, modules []string) {
-	props := fgProps{}
-	props.Name = proptools.StringPtr("all-modules-system-annotations")
-	props.Srcs = createSrcs(modules, "{.system.annotations.zip}")
-	ctx.CreateModule(android.FileGroupFactory, &props)
+func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) {
+	for _, i := range []struct{
+		name    string
+		tag     string
+		modules []string
+	}{
+		{
+			name: "all-modules-public-annotations",
+			tag:  "{.public.annotations.zip}",
+			modules: modules,
+		}, {
+			name: "all-modules-system-annotations",
+			tag:  "{.system.annotations.zip}",
+			modules: modules,
+		}, {
+			name: "all-modules-module-lib-annotations",
+			tag:  "{.module-lib.annotations.zip}",
+			modules: modules,
+		}, {
+			name: "all-modules-system-server-annotations",
+			tag:  "{.system-server.annotations.zip}",
+			modules: system_server_modules,
+		},
+	} {
+		props := fgProps{}
+		props.Name = proptools.StringPtr(i.name)
+		props.Srcs = createSrcs(i.modules, i.tag)
+		ctx.CreateModule(android.FileGroupFactory, &props)
+	}
 }
 
 func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) {
@@ -172,17 +189,43 @@
 	//    difficult to achieve.
 	modules = remove(modules, art)
 
-	props := genruleProps{}
-	props.Name = proptools.StringPtr("api-versions-xml-public-filtered")
-	props.Tools = []string{"api_versions_trimmer"}
-	props.Out = []string{"api-versions-public-filtered.xml"}
-	props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)")
-	// Note: order matters: first parameter is the full api-versions.xml
-	// after that the stubs files in any order
-	// stubs files are all modules that export API surfaces EXCEPT ART
-	props.Srcs = append([]string{":api_versions_public{.api_versions.xml}"}, createSrcs(modules, ".stubs{.jar}")...)
-	props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
-	ctx.CreateModule(genrule.GenRuleFactory, &props)
+	for _, i := range []struct{
+		name string
+		out  string
+		in   string
+	}{
+		{
+			// We shouldn't need public-filtered or system-filtered.
+			// public-filtered is currently used to lint things that
+			// use the module sdk or the system server sdk, but those
+			// should be switched over to module-filtered and
+			// system-server-filtered, and then public-filtered can
+			// be removed.
+			name: "api-versions-xml-public-filtered",
+			out:  "api-versions-public-filtered.xml",
+			in:   ":api_versions_public{.api_versions.xml}",
+		}, {
+			name: "api-versions-xml-module-lib-filtered",
+			out:  "api-versions-module-lib-filtered.xml",
+			in:   ":api_versions_module_lib{.api_versions.xml}",
+		}, {
+			name: "api-versions-xml-system-server-filtered",
+			out:  "api-versions-system-server-filtered.xml",
+			in:   ":api_versions_system_server{.api_versions.xml}",
+		},
+	} {
+		props := genruleProps{}
+		props.Name = proptools.StringPtr(i.name)
+		props.Out = []string{i.out}
+		// Note: order matters: first parameter is the full api-versions.xml
+		// after that the stubs files in any order
+		// stubs files are all modules that export API surfaces EXCEPT ART
+		props.Srcs = append([]string{i.in}, createSrcs(modules, ".stubs{.jar}")...)
+		props.Tools = []string{"api_versions_trimmer"}
+		props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)")
+		props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
+		ctx.CreateModule(genrule.GenRuleFactory, &props)
+	}
 }
 
 func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) {
@@ -279,11 +322,12 @@
 
 func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
 	bootclasspath := a.properties.Bootclasspath
+	system_server_classpath := a.properties.System_server_classpath
 	if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") {
 		bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...)
 		sort.Strings(bootclasspath)
 	}
-	createMergedTxts(ctx, bootclasspath, a.properties.System_server_classpath)
+	createMergedTxts(ctx, bootclasspath, system_server_classpath)
 
 	createMergedStubsSrcjar(ctx, bootclasspath)
 
@@ -292,8 +336,7 @@
 	createMergedFrameworkModuleLibStubs(ctx, bootclasspath)
 	createMergedFrameworkImpl(ctx, bootclasspath)
 
-	createMergedPublicAnnotationsFilegroup(ctx, bootclasspath)
-	createMergedSystemAnnotationsFilegroup(ctx, bootclasspath)
+	createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath)
 
 	createFilteredApiVersions(ctx, bootclasspath)
 
diff --git a/core/api/current.txt b/core/api/current.txt
index 9676451..431f06c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11596,6 +11596,7 @@
     field public static final String ACTION_SESSION_UPDATED = "android.content.pm.action.SESSION_UPDATED";
     field public static final String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
     field public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
+    field public static final String EXTRA_PRE_APPROVAL = "android.content.pm.extra.PRE_APPROVAL";
     field public static final String EXTRA_SESSION = "android.content.pm.extra.SESSION";
     field public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
     field public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
@@ -11652,6 +11653,7 @@
     method public void removeChildSessionId(int);
     method public void removeSplit(@NonNull String) throws java.io.IOException;
     method public void requestChecksums(@NonNull String, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull java.util.concurrent.Executor, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, java.io.FileNotFoundException;
+    method public void requestUserPreapproval(@NonNull android.content.pm.PackageInstaller.PreapprovalDetails, @NonNull android.content.IntentSender);
     method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException;
     method public void setStagingProgress(float);
     method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5e1c234..0b428d9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11162,6 +11162,7 @@
 
   public final class ActivityEvent implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public android.app.assist.ActivityId getActivityId();
     method @NonNull public android.content.ComponentName getComponentName();
     method public int getEventType();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7d19ed4..81aa6da 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -23,6 +23,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager.ProcessCapability;
 import android.app.ActivityManager.RestrictionLevel;
+import android.app.assist.ActivityId;
 import android.content.ComponentName;
 import android.content.IIntentReceiver;
 import android.content.IIntentSender;
@@ -343,7 +344,7 @@
      */
     public abstract void updateActivityUsageStats(
             ComponentName activity, @UserIdInt int userId, int event, IBinder appToken,
-            ComponentName taskRoot);
+            ComponentName taskRoot, ActivityId activityId);
     public abstract void updateForegroundTimeIfOnBattery(
             String packageName, int uid, long cpuTimeDiff);
     public abstract void sendForegroundProfileChanged(@UserIdInt int userId);
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 033cffe..e2082f7 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -650,7 +650,7 @@
             if (decorView != null) {
                 Drawable drawable = decorView.getBackground();
                 if (drawable != null) {
-                    drawable.setAlpha(1);
+                    drawable.setAlpha(255);
                 }
             }
         }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index ddfbc68..302d146 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -159,6 +159,7 @@
     void clearRequestedListenerHints(in INotificationListener token);
     void requestHintsFromListener(in INotificationListener token, int hints);
     int getHintsFromListener(in INotificationListener token);
+    int getHintsFromListenerNoToken();
     void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
     int getInterruptionFilterFromListener(in INotificationListener token);
     void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 392f52a..f6d27ad 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -66,9 +66,9 @@
 
 /**
  * Class to notify the user of events that happen.  This is how you tell
- * the user that something has happened in the background. {@more}
+ * the user that something has happened in the background.
  *
- * Notifications can take different forms:
+ * <p>Notifications can take different forms:
  * <ul>
  *      <li>A persistent icon that goes in the status bar and is accessible
  *          through the launcher, (when the user selects it, a designated Intent
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 8d6c8e8d..1fc6bda 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -20,6 +20,7 @@
 import android.content.pm.DataLoaderParamsParcel;
 import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.IPackageInstallObserver2;
+import android.content.pm.PackageInstaller;
 import android.content.IntentSender;
 import android.os.ParcelFileDescriptor;
 
@@ -58,4 +59,6 @@
 
     boolean isStaged();
     int getInstallFlags();
+
+    void requestUserPreapproval(in PackageInstaller.PreapprovalDetails details, in IntentSender statusReceiver);
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 5b18273..d2fb1fb 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -170,6 +170,10 @@
     /** {@hide} */
     public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
 
+    /** @hide */
+    public static final String ACTION_CONFIRM_PRE_APPROVAL =
+            "android.content.pm.action.CONFIRM_PRE_APPROVAL";
+
     /**
      * An integer session ID that an operation is working with.
      *
@@ -207,6 +211,17 @@
     public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
 
     /**
+     * Indicate if the status is for a pre-approval request.
+     *
+     * If callers use the same {@link IntentSender} for both
+     * {@link Session#requestUserPreapproval(PreapprovalDetails, IntentSender)} and
+     * {@link Session#commit(IntentSender)}, they can use this to differentiate between them.
+     *
+     * @see Intent#getBooleanExtra(String, boolean)
+     */
+    public static final String EXTRA_PRE_APPROVAL = "android.content.pm.extra.PRE_APPROVAL";
+
+    /**
      * Detailed string representation of the status, including raw details that
      * are useful for debugging.
      *
@@ -1667,6 +1682,41 @@
                 e.rethrowFromSystemServer();
             }
         }
+
+        /**
+         * Attempt to request the approval before committing this session.
+         *
+         * For installers that have been granted the
+         * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES REQUEST_INSTALL_PACKAGES}
+         * permission, they can request the approval from users before
+         * {@link Session#commit(IntentSender)} is called. This may require user intervention as
+         * well. The result of the request will be reported through the given callback.
+         *
+         * @param details the adequate context to this session for requesting the approval from
+         *                users prior to commit.
+         * @param statusReceiver called when the state of the session changes.
+         *                       Intents sent to this receiver contain
+         *                       {@link #EXTRA_STATUS}. Refer to the individual
+         *                       status codes on how to handle them.
+         *
+         * @throws IllegalArgumentException when {@link PreapprovalDetails} is {@code null}.
+         * @throws IllegalArgumentException if {@link IntentSender} is {@code null}.
+         * @throws IllegalStateException if called on a multi-package session (no matter
+         *                               the parent session or any of the children sessions).
+         * @throws IllegalStateException if called again after this method has been called on
+         *                               this session.
+         * @throws SecurityException when the caller does not own this session.
+         */
+        public void requestUserPreapproval(@NonNull PreapprovalDetails details,
+                @NonNull IntentSender statusReceiver) {
+            Preconditions.checkArgument(details != null, "preapprovalDetails cannot be null.");
+            Preconditions.checkArgument(statusReceiver != null, "statusReceiver cannot be null.");
+            try {
+                mSession.requestUserPreapproval(details, statusReceiver);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
     }
 
     /**
@@ -2631,6 +2681,9 @@
         /** {@hide} */
         public int installerUid;
 
+        /** @hide */
+        public boolean isPreapprovalRequested;
+
         /** {@hide} */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         public SessionInfo() {
@@ -2678,6 +2731,7 @@
             mSessionErrorCode = source.readInt();
             mSessionErrorMessage = source.readString();
             isCommitted = source.readBoolean();
+            isPreapprovalRequested = source.readBoolean();
             rollbackDataPolicy = source.readInt();
             createdMillis = source.readLong();
             requireUserAction = source.readInt();
@@ -3099,7 +3153,7 @@
         }
 
         /**
-         * Returns the set of session IDs that will be committed when this session is commited if
+         * Returns the set of session IDs that will be committed when this session is committed if
          * this session is a multi-package session.
          */
         @NonNull
@@ -3257,6 +3311,7 @@
             dest.writeInt(mSessionErrorCode);
             dest.writeString(mSessionErrorMessage);
             dest.writeBoolean(isCommitted);
+            dest.writeBoolean(isPreapprovalRequested);
             dest.writeInt(rollbackDataPolicy);
             dest.writeLong(createdMillis);
             dest.writeInt(requireUserAction);
diff --git a/core/java/android/credentials/ui/Entry.java b/core/java/android/credentials/ui/Entry.java
new file mode 100644
index 0000000..122c54a
--- /dev/null
+++ b/core/java/android/credentials/ui/Entry.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2022 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.credentials.ui;
+
+import android.annotation.NonNull;
+import android.app.slice.Slice;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+/**
+ * A credential, save, or action entry to be rendered.
+ *
+ * @hide
+ */
+public class Entry implements Parcelable {
+    // TODO: move to jetpack.
+    public static final String VERSION = "v1";
+    public static final Uri CREDENTIAL_MANAGER_ENTRY_URI = Uri.parse("credentialmanager.slice");
+    public static final String HINT_TITLE = "hint_title";
+    public static final String HINT_SUBTITLE = "hint_subtitle";
+    public static final String HINT_ICON = "hint_icon";
+
+    /**
+    * The intent extra key for the action chip {@code Entry} list when launching the UX activities.
+    */
+    public static final String EXTRA_ENTRY_LIST_ACTION_CHIP =
+            "android.credentials.ui.extra.ENTRY_LIST_ACTION_CHIP";
+    /**
+    * The intent extra key for the credential / save {@code Entry} list when launching the UX
+    * activities.
+    */
+    public static final String EXTRA_ENTRY_LIST_CREDENTIAL =
+            "android.credentials.ui.extra.ENTRY_LIST_CREDENTIAL";
+    /**
+    * The intent extra key for the authentication action {@code Entry} when launching the UX
+    * activities.
+    */
+    public static final String EXTRA_ENTRY_AUTHENTICATION_ACTION =
+            "android.credentials.ui.extra.ENTRY_AUTHENTICATION_ACTION";
+
+    // TODO: may be changed to other type depending on the service implementation.
+    private final int mId;
+
+    @NonNull
+    private final Slice mSlice;
+
+    protected Entry(@NonNull Parcel in) {
+        int entryId = in.readInt();
+        Slice slice = Slice.CREATOR.createFromParcel(in);
+
+        mId = entryId;
+        mSlice = slice;
+        AnnotationValidations.validate(NonNull.class, null, mSlice);
+    }
+
+    public Entry(int id, @NonNull Slice slice) {
+        mId = id;
+        mSlice = slice;
+    }
+
+    /**
+    * Returns the id of this entry that's unique within the context of the CredentialManager
+    * request.
+    */
+    public int getEntryId() {
+        return mId;
+    }
+
+    /**
+    * Returns the Slice to be rendered.
+    */
+    @NonNull
+    public Slice getSlice() {
+        return mSlice;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mId);
+        mSlice.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<Entry> CREATOR = new Creator<Entry>() {
+        @Override
+        public Entry createFromParcel(@NonNull Parcel in) {
+            return new Entry(in);
+        }
+
+        @Override
+        public Entry[] newArray(int size) {
+            return new Entry[size];
+        }
+    };
+}
diff --git a/core/java/android/credentials/ui/ProviderData.java b/core/java/android/credentials/ui/ProviderData.java
index 49e5e49..18e6ba4 100644
--- a/core/java/android/credentials/ui/ProviderData.java
+++ b/core/java/android/credentials/ui/ProviderData.java
@@ -17,11 +17,15 @@
 package android.credentials.ui;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.util.AnnotationValidations;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Holds metadata and credential entries for a single provider.
  *
@@ -36,13 +40,24 @@
     public static final String EXTRA_PROVIDER_DATA_LIST =
             "android.credentials.ui.extra.PROVIDER_DATA_LIST";
 
-    // TODO: add entry data.
-
     @NonNull
     private final String mPackageName;
+    @NonNull
+    private final List<Entry> mCredentialEntries;
+    @NonNull
+    private final List<Entry> mActionChips;
+    @Nullable
+    private final Entry mAuthenticationEntry;
 
-    public ProviderData(@NonNull String packageName) {
+    public ProviderData(
+            @NonNull String packageName,
+            @NonNull List<Entry> credentialEntries,
+            @NonNull List<Entry> actionChips,
+            @Nullable Entry authenticationEntry) {
         mPackageName = packageName;
+        mCredentialEntries = credentialEntries;
+        mActionChips = actionChips;
+        mAuthenticationEntry = authenticationEntry;
     }
 
     /** Returns the provider package name. */
@@ -51,15 +66,46 @@
         return mPackageName;
     }
 
+    @NonNull
+    public List<Entry> getCredentialEntries() {
+        return mCredentialEntries;
+    }
+
+    @NonNull
+    public List<Entry> getActionChips() {
+        return mActionChips;
+    }
+
+    @Nullable
+    public Entry getAuthenticationEntry() {
+        return mAuthenticationEntry;
+    }
+
     protected ProviderData(@NonNull Parcel in) {
         String packageName = in.readString8();
         mPackageName = packageName;
         AnnotationValidations.validate(NonNull.class, null, mPackageName);
+
+        List<Entry> credentialEntries = new ArrayList<>();
+        in.readTypedList(credentialEntries, Entry.CREATOR);
+        mCredentialEntries = credentialEntries;
+        AnnotationValidations.validate(NonNull.class, null, mCredentialEntries);
+
+        List<Entry> actionChips  = new ArrayList<>();
+        in.readTypedList(actionChips, Entry.CREATOR);
+        mActionChips = actionChips;
+        AnnotationValidations.validate(NonNull.class, null, mActionChips);
+
+        Entry authenticationEntry = in.readTypedObject(Entry.CREATOR);
+        mAuthenticationEntry = authenticationEntry;
     }
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString8(mPackageName);
+        dest.writeTypedList(mCredentialEntries);
+        dest.writeTypedList(mActionChips);
+        dest.writeTypedObject(mAuthenticationEntry, flags);
     }
 
     @Override
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 8305843..bc63686 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -299,7 +299,7 @@
             if (isLegacyCompatibilityWalEnabled()) {
                 return SQLiteCompatibilityWalFlags.getWALSyncMode();
             } else {
-                return SQLiteGlobal.getDefaultSyncMode();
+                return SQLiteGlobal.getWALSyncMode();
             }
         } else {
             return SQLiteGlobal.getDefaultSyncMode();
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 5b1973a..8e4a108 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -306,9 +306,9 @@
      * requests are processed in first-in, first-out order and reprocess requests are processed in
      * first-in, first-out order, respectively. However, the processing order of a regular request
      * and a reprocess request in progress is not specified. In other words, a regular request
-     * will always be processed before regular requets that are submitted later. A reprocess request
-     * will always be processed before reprocess requests that are submitted later. However, a
-     * regular request may not be processed before reprocess requests that are submitted later.<p>
+     * will always be processed before regular requests that are submitted later. A reprocess
+     * request will always be processed before reprocess requests that are submitted later. However,
+     * a regular request may not be processed before reprocess requests that are submitted later.<p>
      *
      * <p>Requests submitted through this method have higher priority than
      * those submitted through {@link #setRepeatingRequest} or
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index adeb722..7a55a5c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2017,7 +2017,7 @@
         public static final int EVENT_PACKAGE_INSTALLED = 0x000b;
         // Event for a package being uninstalled.
         public static final int EVENT_PACKAGE_UNINSTALLED = 0x000c;
-        // Event for a package being uninstalled.
+        // Event for an alarm being sent out to an app.
         public static final int EVENT_ALARM = 0x000d;
         // Record that we have decided we need to collect new stats data.
         public static final int EVENT_COLLECT_EXTERNAL_STATS = 0x000e;
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 2c0be87..3bf9ca0 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -115,6 +115,7 @@
     private final int mMaxStreamVolume;
     private boolean mAffectedByRingerMode;
     private boolean mNotificationOrRing;
+    private final boolean mNotifAliasRing;
     private final Receiver mReceiver = new Receiver();
 
     private Handler mHandler;
@@ -179,6 +180,8 @@
         if (mNotificationOrRing) {
             mRingerMode = mAudioManager.getRingerModeInternal();
         }
+        mNotifAliasRing = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_alias_ring_notif_stream_types);
         mZenMode = mNotificationManager.getZenMode();
 
         if (hasAudioProductStrategies()) {
@@ -280,7 +283,15 @@
         if (zenMuted) {
             mSeekBar.setProgress(mLastAudibleStreamVolume, true);
         } else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
-            mSeekBar.setProgress(0, true);
+            /**
+             * the first variable above is preserved and the conditions below are made explicit
+             * so that when user attempts to slide the notification seekbar out of vibrate the
+             * seekbar doesn't wrongly snap back to 0 when the streams aren't aliased
+             */
+            if (mNotifAliasRing || mStreamType == AudioManager.STREAM_RING
+                    || (mStreamType == AudioManager.STREAM_NOTIFICATION && mMuted)) {
+                mSeekBar.setProgress(0, true);
+            }
         } else if (mMuted) {
             mSeekBar.setProgress(0, true);
         } else {
@@ -354,6 +365,7 @@
         // set the time of stop volume
         if ((mStreamType == AudioManager.STREAM_VOICE_CALL
                 || mStreamType == AudioManager.STREAM_RING
+                || (!mNotifAliasRing && mStreamType == AudioManager.STREAM_NOTIFICATION)
                 || mStreamType == AudioManager.STREAM_ALARM)) {
             sStopVolumeTime = java.lang.System.currentTimeMillis();
         }
@@ -632,8 +644,8 @@
         }
 
         private void updateVolumeSlider(int streamType, int streamValue) {
-            final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType)
-                    : (streamType == mStreamType);
+            final boolean streamMatch = mNotifAliasRing && mNotificationOrRing
+                    ? isNotificationOrRing(streamType) : streamType == mStreamType;
             if (mSeekBar != null && streamMatch && streamValue != -1) {
                 final boolean muted = mAudioManager.isStreamMute(mStreamType)
                         || streamValue == 0;
diff --git a/core/java/android/service/contentcapture/ActivityEvent.java b/core/java/android/service/contentcapture/ActivityEvent.java
index d286942..699f42b 100644
--- a/core/java/android/service/contentcapture/ActivityEvent.java
+++ b/core/java/android/service/contentcapture/ActivityEvent.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.app.assist.ActivityId;
 import android.app.usage.UsageEvents.Event;
 import android.content.ComponentName;
 import android.os.Parcel;
@@ -80,14 +81,25 @@
 
     private final @NonNull ComponentName mComponentName;
     private final @ActivityEventType int mType;
+    private final @NonNull ActivityId mActivityId;
 
     /** @hide */
-    public ActivityEvent(@NonNull ComponentName componentName, @ActivityEventType int type) {
+    public ActivityEvent(@NonNull ActivityId activityId,
+            @NonNull ComponentName componentName, @ActivityEventType int type) {
+        mActivityId = activityId;
         mComponentName = componentName;
         mType = type;
     }
 
     /**
+     * Gets the ActivityId of the activity associated with the event.
+     */
+    @NonNull
+    public ActivityId getActivityId() {
+        return mActivityId;
+    }
+
+    /**
      * Gests the {@link ComponentName} of the activity associated with the event.
      */
     @NonNull
@@ -129,6 +141,7 @@
     @Override
     public String toString() {
         return new StringBuilder("ActivityEvent[").append(mComponentName.toShortString())
+                .append(", ActivityId: ").append(mActivityId)
                 .append("]:").append(getTypeAsString(mType)).toString();
     }
 
@@ -141,6 +154,7 @@
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeParcelable(mComponentName, flags);
         parcel.writeInt(mType);
+        parcel.writeParcelable(mActivityId, flags);
     }
 
     public static final @android.annotation.NonNull Creator<ActivityEvent> CREATOR =
@@ -149,9 +163,12 @@
         @Override
         @NonNull
         public ActivityEvent createFromParcel(@NonNull Parcel parcel) {
-            final ComponentName componentName = parcel.readParcelable(null, android.content.ComponentName.class);
+            final ComponentName componentName =
+                    parcel.readParcelable(null, ComponentName.class);
             final int eventType = parcel.readInt();
-            return new ActivityEvent(componentName, eventType);
+            final ActivityId activityId =
+                    parcel.readParcelable(null, ActivityId.class);
+            return new ActivityEvent(activityId, componentName, eventType);
         }
 
         @Override
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 4324442..aa45c20 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -42,8 +42,11 @@
     private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
         @Override
         public void startDream(WindowManager.LayoutParams layoutParams,
-                IDreamOverlayCallback callback) {
+                IDreamOverlayCallback callback, String dreamComponent,
+                boolean shouldShowComplications) {
             mDreamOverlayCallback = callback;
+            mDreamComponent = ComponentName.unflattenFromString(dreamComponent);
+            mShowComplications = shouldShowComplications;
             onStartDream(layoutParams);
         }
     };
@@ -56,10 +59,6 @@
     @Nullable
     @Override
     public final IBinder onBind(@NonNull Intent intent) {
-        mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS,
-                DreamService.DEFAULT_SHOW_COMPLICATIONS);
-        mDreamComponent = intent.getParcelableExtra(DreamService.EXTRA_DREAM_COMPONENT,
-                ComponentName.class);
         return mDreamOverlay.asBinder();
     }
 
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 3b7698e3..3c1fef0 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -214,19 +214,6 @@
     private static final String DREAM_META_DATA_ROOT_TAG = "dream";
 
     /**
-     * Extra containing a boolean for whether to show complications on the overlay.
-     * @hide
-     */
-    public static final String EXTRA_SHOW_COMPLICATIONS =
-            "android.service.dreams.SHOW_COMPLICATIONS";
-
-    /**
-     * Extra containing the component name for the active dream.
-     * @hide
-     */
-    public static final String EXTRA_DREAM_COMPONENT = "android.service.dreams.DREAM_COMPONENT";
-
-    /**
      * The default value for whether to show complications on the overlay.
      *
      * @hide
@@ -252,6 +239,9 @@
 
     private boolean mDebug = false;
 
+    private ComponentName mDreamComponent;
+    private boolean mShouldShowComplications;
+
     private DreamServiceWrapper mDreamServiceWrapper;
     private Runnable mDispatchAfterOnAttachedToWindow;
 
@@ -947,6 +937,11 @@
     @Override
     public void onCreate() {
         if (mDebug) Slog.v(mTag, "onCreate()");
+
+        mDreamComponent = new ComponentName(this, getClass());
+        mShouldShowComplications = fetchShouldShowComplications(this /*context*/,
+                fetchServiceInfo(this /*context*/, mDreamComponent));
+
         super.onCreate();
     }
 
@@ -994,14 +989,7 @@
         // Connect to the overlay service if present.
         if (!mWindowless && overlayComponent != null) {
             final Resources resources = getResources();
-            final ComponentName dreamService = new ComponentName(this, getClass());
-
-            final ServiceInfo serviceInfo = fetchServiceInfo(this, dreamService);
-            final Intent overlayIntent = new Intent()
-                    .setComponent(overlayComponent)
-                    .putExtra(EXTRA_SHOW_COMPLICATIONS,
-                            fetchShouldShowComplications(this, serviceInfo))
-                    .putExtra(EXTRA_DREAM_COMPONENT, dreamService);
+            final Intent overlayIntent = new Intent().setComponent(overlayComponent);
 
             mOverlayConnection = new OverlayConnection(
                     /* context= */ this,
@@ -1364,7 +1352,9 @@
                             // parameters once the window has been attached.
                             mDreamStartOverlayConsumer = overlay -> {
                                 try {
-                                    overlay.startDream(mWindow.getAttributes(), mOverlayCallback);
+                                    overlay.startDream(mWindow.getAttributes(), mOverlayCallback,
+                                            mDreamComponent.flattenToString(),
+                                            mShouldShowComplications);
                                 } catch (RemoteException e) {
                                     Log.e(mTag, "could not send window attributes:" + e);
                                 }
diff --git a/core/java/android/service/dreams/IDreamOverlay.aidl b/core/java/android/service/dreams/IDreamOverlay.aidl
index 2b6633d..05ebbfe 100644
--- a/core/java/android/service/dreams/IDreamOverlay.aidl
+++ b/core/java/android/service/dreams/IDreamOverlay.aidl
@@ -31,7 +31,11 @@
     * @param params The {@link LayoutParams} for the associated DreamWindow, including the window
                     token of the Dream Activity.
     * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the
-    *                 dream.
+    *                dream.
+    * @param dreamComponent The component name of the dream service requesting overlay.
+    * @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock
+    *                and weather.
     */
-    void startDream(in LayoutParams params, in IDreamOverlayCallback callback);
+    void startDream(in LayoutParams params, in IDreamOverlayCallback callback,
+        in String dreamComponent, in boolean shouldShowComplications);
 }
diff --git a/core/java/android/service/voice/AbstractHotwordDetector.java b/core/java/android/service/voice/AbstractHotwordDetector.java
index a2ca5a3..5d3852b 100644
--- a/core/java/android/service/voice/AbstractHotwordDetector.java
+++ b/core/java/android/service/voice/AbstractHotwordDetector.java
@@ -126,21 +126,26 @@
             Slog.d(TAG, "updateState()");
         }
         throwIfDetectorIsNoLongerActive();
-        synchronized (mLock) {
-            updateStateLocked(options, sharedMemory, null /* callback */, mDetectorType);
+        try {
+            mManagerService.updateState(options, sharedMemory);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
-    protected void updateStateLocked(@Nullable PersistableBundle options,
-            @Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback,
+    protected void initAndVerifyDetector(
+            @Nullable PersistableBundle options,
+            @Nullable SharedMemory sharedMemory,
+            @NonNull IHotwordRecognitionStatusCallback callback,
             int detectorType) {
         if (DEBUG) {
-            Slog.d(TAG, "updateStateLocked()");
+            Slog.d(TAG, "initAndVerifyDetector()");
         }
         Identity identity = new Identity();
         identity.packageName = ActivityThread.currentOpPackageName();
         try {
-            mManagerService.updateState(identity, options, sharedMemory, callback, detectorType);
+            mManagerService.initAndVerifyDetector(identity, options, sharedMemory, callback,
+                    detectorType);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index d01e7fe..d58f6d3 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -817,10 +817,8 @@
 
     @Override
     void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
-        // TODO: transition to use an API that is not updateState to provide
-        //  onHotwordDetectionServiceInitialized status to external callback
         if (mSupportHotwordDetectionService) {
-            updateStateLocked(options, sharedMemory, mInternalCallback,
+            initAndVerifyDetector(options, sharedMemory, mInternalCallback,
                     DETECTOR_TYPE_TRUSTED_HOTWORD_DSP);
         }
         try {
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index 02561c94..11688df 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -69,9 +69,7 @@
 
     @Override
     void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
-        // TODO: transition to use an API that is not updateState to provide
-        //  onHotwordDetectionServiceInitialized status to external callback
-        updateStateLocked(options, sharedMemory,
+        initAndVerifyDetector(options, sharedMemory,
                 new InitializationStateListener(mHandler, mCallback),
                 DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE);
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5c899e4..7b6ebf7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15905,15 +15905,16 @@
     }
 
     /**
-     * Returns whether the device is currently in touch mode. Touch mode is entered
-     * once the user begins interacting with the device by touch, and affects various
-     * things like whether focus is always visible to the user.
+     * Returns the touch mode state associated with this view.
      *
-     * If this view has no {@link ViewRootImpl} or {@link Display} attached, then it will return
-     * the default touch mode value defined in
+     * Attached views return the touch mode state from the associated window's display.
+     * Detached views just return the default touch mode value defined in
      * {@code com.android.internal.R.bool.config_defaultInTouchMode}.
      *
-     * @return Whether the device is in touch mode.
+     * Touch mode is entered once the user begins interacting with the device by touch, and
+     * affects various things like whether focus highlight is always visible to the user.
+     *
+     * @return the touch mode state associated with this view
      */
     @ViewDebug.ExportedProperty
     public boolean isInTouchMode() {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index aa3aefd..8a4d451 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2439,7 +2439,11 @@
      * Subclasses override this to specify a default movement method.
      */
     protected MovementMethod getDefaultMovementMethod() {
-        return null;
+        if (isFocusable() || isFocusableInTouchMode()) {
+            return ArrowKeyMovementMethod.getInstance();
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -2654,9 +2658,12 @@
     /**
      * Gets the {@link android.text.method.MovementMethod} being used for this TextView,
      * which provides positioning, scrolling, and text selection functionality.
-     * This will frequently be null for non-EditText TextViews.
+     * By default, this returns an instance of {@link android.text.method.ArrowKeyMovementMethod}
+     * if this View is focusable or focusable in touch mode.
+     *
      * @return the movement method being used for this TextView.
      * @see android.text.method.MovementMethod
+     * @see android.text.method.ArrowKeyMovementMethod
      */
     public final MovementMethod getMovementMethod() {
         return mMovement;
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index fbdd325..1bc8e6d 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -407,6 +407,7 @@
     public static final class Change implements Parcelable {
         private final WindowContainerToken mContainer;
         private WindowContainerToken mParent;
+        private WindowContainerToken mLastParent;
         private final SurfaceControl mLeash;
         private @TransitionMode int mMode = TRANSIT_NONE;
         private @ChangeFlags int mFlags = FLAG_NONE;
@@ -435,6 +436,7 @@
         private Change(Parcel in) {
             mContainer = in.readTypedObject(WindowContainerToken.CREATOR);
             mParent = in.readTypedObject(WindowContainerToken.CREATOR);
+            mLastParent = in.readTypedObject(WindowContainerToken.CREATOR);
             mLeash = new SurfaceControl();
             mLeash.readFromParcel(in);
             mMode = in.readInt();
@@ -458,6 +460,14 @@
             mParent = parent;
         }
 
+        /**
+         * Sets the parent of this change's container before the transition if this change's
+         * container is reparented in the transition.
+         */
+        public void setLastParent(@Nullable WindowContainerToken lastParent) {
+            mLastParent = lastParent;
+        }
+
         /** Sets the transition mode for this change */
         public void setMode(@TransitionMode int mode) {
             mMode = mode;
@@ -541,6 +551,17 @@
             return mParent;
         }
 
+        /**
+         * @return the parent of the changing container before the transition if it is reparented
+         * in the transition. The parent window may not be collected in the transition as a
+         * participant, and it may have been detached from the display. {@code null} if the changing
+         * container has not been reparented in the transition, or if the parent is not organizable.
+         */
+        @Nullable
+        public WindowContainerToken getLastParent() {
+            return mLastParent;
+        }
+
         /** @return which action this change represents. */
         public @TransitionMode int getMode() {
             return mMode;
@@ -640,6 +661,7 @@
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeTypedObject(mContainer, flags);
             dest.writeTypedObject(mParent, flags);
+            dest.writeTypedObject(mLastParent, flags);
             mLeash.writeToParcel(dest, flags);
             dest.writeInt(mMode);
             dest.writeInt(mFlags);
@@ -685,6 +707,7 @@
                     + mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation
                     + " endFixedRotation=" + mEndFixedRotation;
             if (mSnapshot != null) out += " snapshot=" + mSnapshot;
+            if (mLastParent != null) out += " lastParent=" + mLastParent;
             return out + "}";
         }
     }
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index bbcf982..9f23f24 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -245,6 +245,23 @@
 
     /**
      * Set configuration and pass read-only data to hotword detection service.
+     *
+     * @param options Application configuration data to provide to the
+     * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
+     * other contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob to provide to the
+     * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
+     */
+    @EnforcePermission("MANAGE_HOTWORD_DETECTION")
+    void updateState(
+            in PersistableBundle options,
+            in SharedMemory sharedMemory);
+
+    /**
+     * Set configuration and pass read-only data to hotword detection service when creating
+     * the detector.
+     *
      * Caller must provide an identity, used for permission tracking purposes.
      * The uid/pid elements of the identity will be ignored by the server and replaced with the ones
      * provided by binder.
@@ -259,7 +276,7 @@
      * @param detectorType Indicate which detector is used.
      */
     @EnforcePermission("MANAGE_HOTWORD_DETECTION")
-    void updateState(
+    void initAndVerifyDetector(
             in Identity originatorIdentity,
             in PersistableBundle options,
             in SharedMemory sharedMemory,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3834478..90d84dd 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -658,6 +658,20 @@
         -->
     </integer-array>
 
+    <!-- The device states (supplied by DeviceStateManager) that should be treated as half-folded by
+     the display fold controller. Default is empty. -->
+    <integer-array name="config_halfFoldedDeviceStates">
+        <!-- Example:
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        -->
+    </integer-array>
+
+    <!-- Indicates whether the window manager reacts to half-fold device states by overriding
+     rotation. -->
+    <bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool>
+
     <!-- When a device enters any of these states, it should be woken up. States are defined in
          device_state_configuration.xml. -->
     <integer-array name="config_deviceStatesOnWhichToWakeUp">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2fbf803..153e499 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1626,6 +1626,7 @@
   <java-symbol type="xml" name="password_kbd_symbols_shift" />
   <java-symbol type="xml" name="power_profile" />
   <java-symbol type="xml" name="power_profile_test" />
+  <java-symbol type="xml" name="irq_device_map" />
   <java-symbol type="xml" name="sms_short_codes" />
   <java-symbol type="xml" name="audio_assets" />
   <java-symbol type="xml" name="global_keys" />
@@ -4015,6 +4016,8 @@
 
   <!-- For Foldables -->
   <java-symbol type="array" name="config_foldedDeviceStates" />
+  <java-symbol type="array" name="config_halfFoldedDeviceStates" />
+  <java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" />
   <java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
   <java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />
   <java-symbol type="string" name="config_foldedArea" />
diff --git a/core/res/res/xml/irq_device_map.xml b/core/res/res/xml/irq_device_map.xml
new file mode 100644
index 0000000..86a44d6
--- /dev/null
+++ b/core/res/res/xml/irq_device_map.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, 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.
+*/
+-->
+<irq-device-map>
+  <!--  This file maps devices (chips) that can send IRQs to the CPU (and bring it out of sleep) to
+        logical subsystems in userspace code. Since each Android device has its own uniquely
+        designed chipset, this mapping is expected to be empty by default and should be overridden
+        by device specific configs.
+        This mapping helps the system to meaningfully attribute CPU wakeups to logical work that
+        happened on the device. The devices are referred to by their names as defined in the kernel.
+        Currently defined subsystems are:
+        - Alarm: Use this to denote wakeup alarms requested by apps via the AlarmManager API.
+
+        The overlay should use tags <device> and <subsystem> to describe this mapping in the
+        following way:
+
+        <irq-device-map>
+            <device name="device_name_1">
+                <subsystem>Subsystem1</subsystem>
+                <subsystem>Subsystem2</subsystem>
+                :
+                :
+            </device>
+            <device name="device_name_2">
+                :
+            </device>
+            :
+        </irq-device-map>
+
+        The tag <device> should have a "name" attribute specifying the kernel name of the device.
+        Each <device> tag can then enclose multiple <subsystem> tags. Each <subsystem> tag should
+        enclose the name of the logical subsystems (one of the ones defined above) as text.
+        Undefined subsystem names will be ignored by the framework.
+  -->
+</irq-device-map>
\ No newline at end of file
diff --git a/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java b/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java
index 6bd498c..d633843 100644
--- a/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java
+++ b/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java
@@ -124,7 +124,7 @@
         assertTransportBehavior(input, expected);
     }
 
-    public void testMutiplePingPing() {
+    public void testMultiplePingPing() {
         final byte[] input = concat(
                 generatePacket(MESSAGE_REQUEST_PING, /* sequence */ 1, "red"),
                 generatePacket(MESSAGE_REQUEST_PING, /* sequence */ 2, "green"));
@@ -248,7 +248,7 @@
         }
 
         @Override
-        public int read(byte b[], int off, int len) throws IOException {
+        public int read(byte[] b, int off, int len) throws IOException {
             // Instead of hanging indefinitely, wait a bit and claim that
             // nothing was read, without hitting EOF
             SystemClock.sleep(100);
@@ -259,13 +259,13 @@
     private static class DelayingInputStream extends FilterInputStream {
         private final long mDelay;
 
-        public DelayingInputStream(InputStream in, long delay) {
+        DelayingInputStream(InputStream in, long delay) {
             super(in);
             mDelay = delay;
         }
 
         @Override
-        public int read(byte b[], int off, int len) throws IOException {
+        public int read(byte[] b, int off, int len) throws IOException {
             SystemClock.sleep(mDelay);
             return super.read(b, off, len);
         }
@@ -274,25 +274,25 @@
     private static class DelayingOutputStream extends FilterOutputStream {
         private final long mDelay;
 
-        public DelayingOutputStream(OutputStream out, long delay) {
+        DelayingOutputStream(OutputStream out, long delay) {
             super(out);
             mDelay = delay;
         }
 
         @Override
-        public void write(byte b[], int off, int len) throws IOException {
+        public void write(byte[] b, int off, int len) throws IOException {
             SystemClock.sleep(mDelay);
             super.write(b, off, len);
         }
     }
 
     private static class TrickleInputStream extends FilterInputStream {
-        public TrickleInputStream(InputStream in) {
+        TrickleInputStream(InputStream in) {
             super(in);
         }
 
         @Override
-        public int read(byte b[], int off, int len) throws IOException {
+        public int read(byte[] b, int off, int len) throws IOException {
             return super.read(b, off, 1);
         }
     }
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index e154586..f6216fa 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1654,6 +1654,12 @@
             android:exported="true">
         </activity>
 
+        <activity android:name="android.app.activity.ActivityTransitionDrawableTest$TestActivity"
+                  android:exported="true"
+                  android:enabled="true"
+                  android:theme="@style/Theme">
+        </activity>
+
         <activity
             android:name="android.os.TestVrActivity"
             android:enableVrMode="com.android.frameworks.coretests/android.os.TestVrActivity$TestVrListenerService">
diff --git a/core/tests/coretests/src/android/app/activity/ActivityTransitionDrawableTest.java b/core/tests/coretests/src/android/app/activity/ActivityTransitionDrawableTest.java
new file mode 100644
index 0000000..2c4e443
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ActivityTransitionDrawableTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2022 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.activity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
+import android.transition.Fade;
+import android.view.View;
+import android.view.Window;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test for verifying Activity Transitions Drawable behavior
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+@Presubmit
+public class ActivityTransitionDrawableTest {
+    private static final String LAUNCH_ON_START = "launch on start";
+
+    @Rule
+    public final ActivityTestRule<TestActivity> mActivityTestRule =
+            new ActivityTestRule<>(TestActivity.class, true);
+
+    @Test
+    public void stopTransitionDrawableAlphaRestored() throws Throwable {
+        mActivityTestRule.runOnUiThread(() -> {
+            Activity activity = mActivityTestRule.getActivity();
+            Intent intent = new Intent(activity, TestActivity.class);
+            intent.putExtra(LAUNCH_ON_START, true);
+            Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle();
+            activity.startActivity(intent, bundle);
+        });
+
+        assertThat(TestActivity.activityAdded.await(5, TimeUnit.SECONDS)).isTrue();
+        TestActivity topActivity = TestActivity.sInstances.get(2);
+        TestActivity middleActivity = TestActivity.sInstances.get(1);
+        assertThat(topActivity.startedLatch.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(middleActivity.stoppedLatch.await(5, TimeUnit.SECONDS)).isTrue();
+        mActivityTestRule.runOnUiThread(() -> {
+            assertThat(middleActivity.getWindow().getDecorView().getBackground().getAlpha())
+                    .isEqualTo(255);
+        });
+    }
+
+    public static class TestActivity extends Activity {
+        public static final ArrayList<TestActivity> sInstances = new ArrayList<TestActivity>();
+        public static CountDownLatch activityAdded = new CountDownLatch(3);
+
+        private boolean mLaunchOnStart = false;
+        public CountDownLatch startedLatch = new CountDownLatch(1);
+        public CountDownLatch stoppedLatch = new CountDownLatch(1);
+
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
+            getWindow().setAllowEnterTransitionOverlap(false);
+            setContentView(new View(this));
+            Fade longFade = new Fade();
+            longFade.setDuration(2000);
+            getWindow().setEnterTransition(longFade);
+            getWindow().setExitTransition(longFade);
+            super.onCreate(savedInstanceState);
+            mLaunchOnStart = getIntent().getBooleanExtra(LAUNCH_ON_START, false);
+            sInstances.add(this);
+            activityAdded.countDown();
+        }
+
+        @Override
+        protected void onStart() {
+            super.onStart();
+            if (mLaunchOnStart) {
+                mLaunchOnStart = false;
+                Intent intent = new Intent(this, TestActivity.class);
+                startActivity(intent);
+            }
+            startedLatch.countDown();
+        }
+
+        @Override
+        protected void onStop() {
+            super.onStop();
+            stoppedLatch.countDown();
+        }
+
+        @Override
+        protected void onDestroy() {
+            super.onDestroy();
+            sInstances.remove(this);
+        }
+    }
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index f9f2906..ca543f4 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1105,6 +1105,12 @@
       "group": "WM_DEBUG_FOCUS",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-1043981272": {
+      "message": "Reverting orientation. Rotating to %s from %s rather than %s.",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayRotation.java"
+    },
     "-1042574499": {
       "message": "Attempted to add Accessibility overlay window with unknown token %s.  Aborting.",
       "level": "WARN",
@@ -4303,6 +4309,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "2066210760": {
+      "message": "foldStateChanged: displayId %d, halfFoldStateChanged %s, saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, mLastOrientation: %d, mRotation: %d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayRotation.java"
+    },
     "2070726247": {
       "message": "InsetsSource updateVisibility for %s, serverVisible: %s clientVisible: %s",
       "level": "DEBUG",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 31a9592..1174b68 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -94,7 +94,7 @@
         ActivityEmbeddingComponent {
     static final String TAG = "SplitController";
     static final boolean ENABLE_SHELL_TRANSITIONS =
-            SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+            SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
 
     @VisibleForTesting
     @GuardedBy("mLock")
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 0fb6ff8..b516e140 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -117,9 +117,7 @@
         if (mWindowLayoutChangeListeners.containsKey(context)
                 // In theory this method can be called on the same consumer with different context.
                 || mWindowLayoutChangeListeners.containsValue(consumer)) {
-            throw new IllegalArgumentException(
-                    "Context or Consumer has already been registered for WindowLayoutInfo"
-                            + " callback.");
+            return;
         }
         if (!context.isUiContext()) {
             throw new IllegalArgumentException("Context must be a UI Context, which should be"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 33761d2..2b36b4c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -452,14 +452,17 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback,
             @NonNull TaskInfo taskInfo, @Nullable TransitionInfo.Change pipTaskChange) {
         TransitionInfo.Change pipChange = pipTaskChange;
-        if (pipChange == null) {
+        if (mCurrentPipTaskToken == null) {
+            ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                    "%s: There is no existing PiP Task for TRANSIT_EXIT_PIP", TAG);
+        } else if (pipChange == null) {
             // The pipTaskChange is null, this can happen if we are reparenting the PIP activity
             // back to its original Task. In that case, we should animate the activity leash
-            // instead, which should be the only non-task, independent, TRANSIT_CHANGE window.
+            // instead, which should be the change whose last parent is the recorded PiP Task.
             for (int i = info.getChanges().size() - 1; i >= 0; --i) {
                 final TransitionInfo.Change change = info.getChanges().get(i);
-                if (change.getTaskInfo() == null && change.getMode() == TRANSIT_CHANGE
-                        && TransitionInfo.isIndependent(change, info)) {
+                if (mCurrentPipTaskToken.equals(change.getLastParent())) {
+                    // Find the activity that is exiting PiP.
                     pipChange = change;
                     break;
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 1372f4d..aaaccd8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -81,7 +81,7 @@
 
     /** Set to {@code true} to enable shell transitions. */
     public static final boolean ENABLE_SHELL_TRANSITIONS =
-            SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+            SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
     public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
             && SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 1a1bebd..5ee8bf3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -61,7 +61,7 @@
 @RunWith(AndroidJUnit4.class)
 public final class StageTaskListenerTests extends ShellTestCase {
     private static final boolean ENABLE_SHELL_TRANSITIONS =
-            SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+            SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
 
     @Mock
     private ShellTaskOrganizer mTaskOrganizer;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 5e8a623..46fbe7c 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1020,6 +1020,40 @@
     return base::unexpected(std::nullopt);
 }
 
+template <typename TChar, typename SP>
+base::expected<size_t, NullOrIOError> ResStringPool::stringIndex(
+        SP sp, std::unordered_map<SP, size_t>& map) const
+{
+    AutoMutex lock(mStringIndexLock);
+
+    if (map.empty()) {
+        // build string index on the first call
+        for (size_t i = 0; i < mHeader->stringCount; i++) {
+            base::expected<SP, NullOrIOError> s;
+            if constexpr(std::is_same_v<TChar, char16_t>) {
+                s = stringAt(i);
+            } else {
+                s = string8At(i);
+            }
+            if (s.has_value()) {
+                const auto r = map.insert({*s, i});
+                if (!r.second) {
+                    ALOGE("failed to build string index, string id=%zu\n", i);
+                }
+            } else {
+                return base::unexpected(s.error());
+            }
+        }
+    }
+
+    if (!map.empty()) {
+        const auto result = map.find(sp);
+        if (result != map.end())
+            return result->second;
+    }
+    return base::unexpected(std::nullopt);
+}
+
 base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_t* str,
                                                                    size_t strLen) const
 {
@@ -1027,134 +1061,28 @@
         return base::unexpected(std::nullopt);
     }
 
-    if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) {
-        if (kDebugStringPoolNoisy) {
-            ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string());
-        }
+    if (kDebugStringPoolNoisy) {
+        ALOGI("indexOfString (%s): %s", isUTF8() ? "UTF-8" : "UTF-16",
+                String8(str, strLen).string());
+    }
 
-        // The string pool contains UTF 8 strings; we don't want to cause
-        // temporary UTF-16 strings to be created as we search.
-        if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
-            // Do a binary search for the string...  this is a little tricky,
-            // because the strings are sorted with strzcmp16().  So to match
-            // the ordering, we need to convert strings in the pool to UTF-16.
-            // But we don't want to hit the cache, so instead we will have a
-            // local temporary allocation for the conversions.
-            size_t convBufferLen = strLen + 4;
-            std::vector<char16_t> convBuffer(convBufferLen);
-            ssize_t l = 0;
-            ssize_t h = mHeader->stringCount-1;
-
-            ssize_t mid;
-            while (l <= h) {
-                mid = l + (h - l)/2;
-                int c = -1;
-                const base::expected<StringPiece, NullOrIOError> s = string8At(mid);
-                if (UNLIKELY(IsIOError(s))) {
-                    return base::unexpected(s.error());
-                }
-                if (s.has_value()) {
-                    char16_t* end = utf8_to_utf16(reinterpret_cast<const uint8_t*>(s->data()),
-                                                  s->size(), convBuffer.data(), convBufferLen);
-                    c = strzcmp16(convBuffer.data(), end-convBuffer.data(), str, strLen);
-                }
-                if (kDebugStringPoolNoisy) {
-                    ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
-                          s->data(), c, (int)l, (int)mid, (int)h);
-                }
-                if (c == 0) {
-                    if (kDebugStringPoolNoisy) {
-                        ALOGI("MATCH!");
-                    }
-                    return mid;
-                } else if (c < 0) {
-                    l = mid + 1;
-                } else {
-                    h = mid - 1;
-                }
-            }
-        } else {
-            // It is unusual to get the ID from an unsorted string block...
-            // most often this happens because we want to get IDs for style
-            // span tags; since those always appear at the end of the string
-            // block, start searching at the back.
-            String8 str8(str, strLen);
-            const size_t str8Len = str8.size();
-            for (int i=mHeader->stringCount-1; i>=0; i--) {
-                const base::expected<StringPiece, NullOrIOError> s = string8At(i);
-                if (UNLIKELY(IsIOError(s))) {
-                    return base::unexpected(s.error());
-                }
-                if (s.has_value()) {
-                    if (kDebugStringPoolNoisy) {
-                        ALOGI("Looking at %s, i=%d\n", s->data(), i);
-                    }
-                    if (str8Len == s->size()
-                            && memcmp(s->data(), str8.string(), str8Len) == 0) {
-                        if (kDebugStringPoolNoisy) {
-                            ALOGI("MATCH!");
-                        }
-                        return i;
-                    }
-                }
-            }
-        }
-
+    base::expected<size_t, NullOrIOError> idx;
+    if (isUTF8()) {
+        auto str8 = String8(str, strLen);
+        idx = stringIndex<char>(StringPiece(str8.c_str(), str8.size()), mStringIndex8);
     } else {
+        idx = stringIndex<char16_t>(StringPiece16(str, strLen), mStringIndex16);
+    }
+
+    if (UNLIKELY(!idx.has_value())) {
+        return base::unexpected(idx.error());
+    }
+
+    if (*idx < mHeader->stringCount) {
         if (kDebugStringPoolNoisy) {
-            ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string());
+            ALOGI("MATCH! (idx=%zu)", *idx);
         }
-
-        if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
-            // Do a binary search for the string...
-            ssize_t l = 0;
-            ssize_t h = mHeader->stringCount-1;
-
-            ssize_t mid;
-            while (l <= h) {
-                mid = l + (h - l)/2;
-                const base::expected<StringPiece16, NullOrIOError> s = stringAt(mid);
-                if (UNLIKELY(IsIOError(s))) {
-                    return base::unexpected(s.error());
-                }
-                int c = s.has_value() ? strzcmp16(s->data(), s->size(), str, strLen) : -1;
-                if (kDebugStringPoolNoisy) {
-                    ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
-                          String8(s->data(), s->size()).string(), c, (int)l, (int)mid, (int)h);
-                }
-                if (c == 0) {
-                    if (kDebugStringPoolNoisy) {
-                        ALOGI("MATCH!");
-                    }
-                    return mid;
-                } else if (c < 0) {
-                    l = mid + 1;
-                } else {
-                    h = mid - 1;
-                }
-            }
-        } else {
-            // It is unusual to get the ID from an unsorted string block...
-            // most often this happens because we want to get IDs for style
-            // span tags; since those always appear at the end of the string
-            // block, start searching at the back.
-            for (int i=mHeader->stringCount-1; i>=0; i--) {
-                const base::expected<StringPiece16, NullOrIOError> s = stringAt(i);
-                if (UNLIKELY(IsIOError(s))) {
-                    return base::unexpected(s.error());
-                }
-                if (kDebugStringPoolNoisy) {
-                    ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).string(), i);
-                }
-                if (s.has_value() && strLen == s->size() &&
-                        strzcmp16(s->data(), s->size(), str, strLen) == 0) {
-                    if (kDebugStringPoolNoisy) {
-                        ALOGI("MATCH!");
-                    }
-                    return i;
-                }
-            }
-        }
+        return *idx;
     }
     return base::unexpected(std::nullopt);
 }
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 9309091..24628cd 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -41,6 +41,7 @@
 #include <array>
 #include <map>
 #include <memory>
+#include <unordered_map>
 
 namespace android {
 
@@ -562,8 +563,17 @@
     incfs::map_ptr<uint32_t>                      mStyles;
     uint32_t                                      mStylePoolSize;    // number of uint32_t
 
+    // mStringIndex is used to quickly map a string to its ID
+    mutable Mutex                                       mStringIndexLock;
+    mutable std::unordered_map<StringPiece, size_t>     mStringIndex8;
+    mutable std::unordered_map<StringPiece16, size_t>   mStringIndex16;
+
     base::expected<StringPiece, NullOrIOError> stringDecodeAt(
         size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const;
+
+    template <typename TChar, typename SP=BasicStringPiece<TChar>>
+    base::expected<size_t, NullOrIOError> stringIndex(
+        SP str, std::unordered_map<SP, size_t>& map) const;
 };
 
 /**
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 50aa6fe..6ae7dfb 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -44,6 +44,7 @@
     void onRequestStreamVolume(int seq);
     void onRequestTrackInfoList(int seq);
     void onRequestCurrentTvInputId(int seq);
+    void onRequestStartRecording(in Uri programUri, int seq);
     void onRequestSigning(
             in String id, in String algorithm, in String alias, in byte[] data, int seq);
     void onAdRequest(in AdRequest request, int Seq);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index c8c695f..84b9c9e 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -63,6 +63,7 @@
     void notifyContentAllowed(in IBinder sessionToken, int userId);
     void notifyContentBlocked(in IBinder sessionToken, in String rating, int userId);
     void notifySignalStrength(in IBinder sessionToken, int stength, int userId);
+    void notifyRecordingStarted(in IBinder sessionToken, in String recordingId, int userId);
     void setSurface(in IBinder sessionToken, in Surface surface, int userId);
     void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
             int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
index 818c287..95b4ffa 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
@@ -53,6 +53,7 @@
     void notifyContentAllowed();
     void notifyContentBlocked(in String rating);
     void notifySignalStrength(int strength);
+    void notifyRecordingStarted(in String recordingId);
     void setSurface(in Surface surface);
     void dispatchSurfaceChanged(int format, int width, int height);
     void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index 32b08b7..6478057 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -43,6 +43,7 @@
     void onRequestStreamVolume();
     void onRequestTrackInfoList();
     void onRequestCurrentTvInputId();
+    void onRequestStartRecording(in Uri programUri);
     void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data);
     void onAdRequest(in AdRequest request);
 }
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index a72f34c..042cb15 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -81,6 +81,7 @@
     private static final int DO_CREATE_MEDIA_VIEW = 27;
     private static final int DO_RELAYOUT_MEDIA_VIEW = 28;
     private static final int DO_REMOVE_MEDIA_VIEW = 29;
+    private static final int DO_NOTIFY_RECORDING_STARTED = 30;
 
     private final HandlerCaller mCaller;
     private Session mSessionImpl;
@@ -164,6 +165,10 @@
                 mSessionImpl.sendCurrentTvInputId((String) msg.obj);
                 break;
             }
+            case DO_NOTIFY_RECORDING_STARTED: {
+                mSessionImpl.notifyRecordingStarted((String) msg.obj);
+                break;
+            }
             case DO_SEND_SIGNING_RESULT: {
                 SomeArgs args = (SomeArgs) msg.obj;
                 mSessionImpl.sendSigningResult((String) args.arg1, (byte[]) args.arg2);
@@ -381,6 +386,12 @@
     }
 
     @Override
+    public void notifyRecordingStarted(String recordingId) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(
+                DO_NOTIFY_RECORDING_STARTED, recordingId));
+    }
+
+    @Override
     public void setSurface(Surface surface) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_SURFACE, surface));
     }
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index c0261f2..a27fd10 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -487,6 +487,18 @@
             }
 
             @Override
+            public void onRequestStartRecording(Uri programUri, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postRequestStartRecording(programUri);
+                }
+            }
+
+            @Override
             public void onRequestSigning(
                     String id, String algorithm, String alias, byte[] data, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
@@ -1035,6 +1047,18 @@
             }
         }
 
+        void notifyRecordingStarted(@Nullable String recordingId) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyRecordingStarted(mToken, recordingId, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
         void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
@@ -1696,6 +1720,15 @@
             });
         }
 
+        void postRequestStartRecording(Uri programUri) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onRequestStartRecording(mSession, programUri);
+                }
+            });
+        }
+
         void postRequestSigning(String id, String algorithm, String alias, byte[] data) {
             mHandler.post(new Runnable() {
                 @Override
@@ -1847,6 +1880,15 @@
         }
 
         /**
+         * This is called when {@link TvInteractiveAppService.Session#RequestStartRecording} is
+         * called.
+         *
+         * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
+         */
+        public void onRequestStartRecording(Session session, Uri programUri) {
+        }
+
+        /**
          * This is called when
          * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is
          * called.
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index f5b4322..3d65effa 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -455,6 +455,13 @@
         }
 
         /**
+         * Receives started recording's ID.
+         * @hide
+         */
+        public void onRecordingStarted(@Nullable String recordingId) {
+        }
+
+        /**
          * Receives signing result.
          * @param signingId the ID to identify the request. It's the same as the corresponding ID in
          *        {@link Session#requestSigning(String, String, String, byte[])}
@@ -905,6 +912,27 @@
         }
 
         /**
+         * Requests starting of recording
+         *
+         * @hide
+         */
+        @CallSuper
+        public void requestStartRecording(@Nullable Uri programUri) {
+            executeOrPostRunnableOnMainThread(() -> {
+                try {
+                    if (DEBUG) {
+                        Log.d(TAG, "requestStartRecording");
+                    }
+                    if (mSessionCallback != null) {
+                        mSessionCallback.onRequestStartRecording(programUri);
+                    }
+                } catch (RemoteException e) {
+                    Log.w(TAG, "error in requestStartRecording", e);
+                }
+            });
+        }
+
+        /**
          * Requests signing of the given data.
          *
          * <p>This is used when the corresponding server of the broadcast-independent interactive
@@ -1114,6 +1142,10 @@
             onAdResponse(response);
         }
 
+        void notifyRecordingStarted(String recordingId) {
+            onRecordingStarted(recordingId);
+        }
+
         /**
          * Notifies when the session state is changed.
          *
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 53b0324..76ba69c 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -581,6 +581,22 @@
     }
 
     /**
+     * Alerts the TV interactive app that a recording has been started with recordingId
+     *
+     * @param recordingId The Id of the recording started
+     *
+     * @hide
+     */
+    public void notifyRecordingStarted(@Nullable String recordingId) {
+        if (DEBUG) {
+            Log.d(TAG, "notifyRecordingStarted");
+        }
+        if (mSession != null) {
+            mSession.notifyRecordingStarted(recordingId);
+        }
+    }
+
+    /**
      * Sends signing result to related TV interactive app.
      *
      * <p>This is used when the corresponding server of the broadcast-independent interactive
@@ -841,6 +857,19 @@
         }
 
         /**
+         * This is called when {@link TvInteractiveAppService.Session#requestStartRecording(Uri)}
+         * is called.
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @param programUri The program URI to record
+         *
+         * @hide
+         */
+        public void onRequestStartRecording(
+                @NonNull String iAppServiceId,
+                @Nullable Uri programUri) {
+        }
+
+        /**
          * This is called when
          * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is
          * called.
@@ -1164,6 +1193,20 @@
         }
 
         @Override
+        public void onRequestStartRecording(Session session, Uri programUri) {
+            if (DEBUG) {
+                Log.d(TAG, "onRequestStartRecording");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onRequestStartRecording - session not created");
+                return;
+            }
+            if (mCallback != null) {
+                mCallback.onRequestStartRecording(mIAppServiceId, programUri);
+            }
+        }
+
+        @Override
         public void onRequestSigning(
                 Session session, String id, String algorithm, String alias, byte[] data) {
             if (DEBUG) {
diff --git a/packages/BackupEncryption/Android.bp b/packages/BackupEncryption/Android.bp
deleted file mode 100644
index 0244f28..0000000
--- a/packages/BackupEncryption/Android.bp
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// 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 {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_app {
-    name: "BackupEncryption",
-    defaults: ["platform_app_defaults"],
-    srcs: ["src/**/*.java"],
-    static_libs: ["backup-encryption-protos", "backuplib"],
-    optimize: { enabled: false },
-    platform_apis: true,
-    certificate: "platform",
-    privileged: true,
-}
-
-java_library {
-    name: "backup-encryption-protos",
-    proto: { type: "nano" },
-    srcs: ["proto/**/*.proto"],
-}
diff --git a/packages/BackupEncryption/AndroidManifest.xml b/packages/BackupEncryption/AndroidManifest.xml
deleted file mode 100644
index 4d174e3..0000000
--- a/packages/BackupEncryption/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (c) 2016 Google Inc.
- *
- * 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.
- */
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.backup.encryption"
-    android:sharedUserId="android.uid.system" >
-
-    <application android:allowBackup="false" >
-        <!-- This service does not need to be exported because it shares uid with the system server
-        which is the only client. -->
-        <service android:name=".BackupEncryptionService"
-                 android:exported="false">
-            <intent-filter>
-                <action android:name="android.encryption.BACKUP_ENCRYPTION" />
-            </intent-filter>
-        </service>
-    </application>
-</manifest>
diff --git a/packages/BackupEncryption/OWNERS b/packages/BackupEncryption/OWNERS
deleted file mode 100644
index d99779e..0000000
--- a/packages/BackupEncryption/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/backup/OWNERS
diff --git a/packages/BackupEncryption/proguard.flags b/packages/BackupEncryption/proguard.flags
deleted file mode 100644
index 851ce8c..0000000
--- a/packages/BackupEncryption/proguard.flags
+++ /dev/null
@@ -1 +0,0 @@
--keep class com.android.server.backup.encryption
diff --git a/packages/BackupEncryption/proto/backup_chunks_metadata.proto b/packages/BackupEncryption/proto/backup_chunks_metadata.proto
deleted file mode 100644
index 2fdedbf..0000000
--- a/packages/BackupEncryption/proto/backup_chunks_metadata.proto
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2018 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
- */
-
-syntax = "proto2";
-
-package android_backup_crypto;
-
-option java_package = "com.android.server.backup.encryption.protos";
-option java_outer_classname = "ChunksMetadataProto";
-
-// Cipher type with which the chunks are encrypted. For now we only support AES/GCM/NoPadding, but
-// this is for backwards-compatibility in case we need to change the default Cipher in the future.
-enum CipherType {
-    UNKNOWN_CIPHER_TYPE = 0;
-    // Chunk is prefixed with a 12-byte nonce. The tag length is 16 bytes.
-    AES_256_GCM = 1;
-}
-
-// Checksum type with which the plaintext is verified.
-enum ChecksumType {
-    UNKNOWN_CHECKSUM_TYPE = 0;
-    SHA_256 = 1;
-}
-
-enum ChunkOrderingType {
-    CHUNK_ORDERING_TYPE_UNSPECIFIED = 0;
-    // The chunk ordering contains a list of the start position of each chunk in the encrypted file,
-    // ordered as in the plaintext file. This allows us to recreate the original plaintext file
-    // during decryption. We use this mode for full backups where the order of the data in the file
-    // is important.
-    EXPLICIT_STARTS = 1;
-    // The chunk ordering does not contain any start positions, and instead each encrypted chunk in
-    // the backup file is prefixed with its length. This allows us to decrypt each chunk but does
-    // not give any information about the order. However, we use this mode for key value backups
-    // where the order does not matter.
-    INLINE_LENGTHS = 2;
-}
-
-// Chunk entry (for local state)
-message Chunk {
-    // SHA-256 MAC of the plaintext of the chunk
-    optional bytes hash = 1;
-    // Number of bytes in encrypted chunk
-    optional int32 length = 2;
-}
-
-// List of the chunks in the blob, along with the length of each chunk. From this is it possible to
-// extract individual chunks. (i.e., start position is equal to the sum of the lengths of all
-// preceding chunks.)
-//
-// This is local state stored on the device. It is never sent to the backup server. See
-// ChunkOrdering for how the device restores the chunks in the correct order.
-// Next tag : 6
-message ChunkListing {
-    repeated Chunk chunks = 1;
-
-    // Cipher algorithm with which the chunks are encrypted.
-    optional CipherType cipher_type = 2;
-
-    // Defines the type of chunk order used to encode the backup file on the server, so that we can
-    // consistently use the same type between backups. If unspecified this backup file was created
-    // before INLINE_LENGTHS was supported, thus assume it is EXPLICIT_STARTS.
-    optional ChunkOrderingType chunk_ordering_type = 5;
-
-    // The document ID returned from Scotty server after uploading the blob associated with this
-    // listing. This needs to be sent when uploading new diff scripts.
-    optional string document_id = 3;
-
-    // Fingerprint mixer salt used for content defined chunking. This is randomly generated for each
-    // package during the initial non-incremental backup and reused for incremental backups.
-    optional bytes fingerprint_mixer_salt = 4;
-}
-
-// Ordering information about plaintext and checksum. This is used on restore to reconstruct the
-// blob in its correct order. (The chunk order is randomized so as to give the server less
-// information about which parts of the backup are changing over time.) This proto is encrypted
-// before being uploaded to the server, with a key unknown to the server.
-message ChunkOrdering {
-    // For backups where ChunksMetadata#chunk_ordering_type = EXPLICIT STARTS:
-    // Ordered start positions of chunks. i.e., the file is the chunk starting at this position,
-    // followed by the chunk starting at this position, followed by ... etc. You can compute the
-    // lengths of the chunks by sorting this list then looking at the start position of the next
-    // chunk after the chunk you care about. This is guaranteed to work as all chunks are
-    // represented in this list.
-    //
-    // For backups where ChunksMetadata#chunk_ordering_type = INLINE_LENGTHS:
-    // This field is unused. See ChunkOrderingType#INLINE_LENGTHS.
-    repeated int32 starts = 1 [packed = true];
-
-    // Checksum of plaintext content. (i.e., in correct order.)
-    //
-    // Each chunk also has a MAC, as generated by GCM, so this is NOT Mac-then-Encrypt, which has
-    // security implications. This is an additional checksum to verify that once the chunks have
-    // been reordered, that the file matches the expected plaintext. This prevents the device
-    // restoring garbage data in case of a mismatch between the ChunkOrdering and the backup blob.
-    optional bytes checksum = 2;
-}
-
-// Additional metadata about a backup blob that needs to be synced to the server. This is used on
-// restore to reconstruct the blob in its correct order. (The chunk order is randomized so as to
-// give the server less information about which parts of the backup are changing over time.) This
-// data structure is only ever uploaded to the server encrypted with a key unknown to the server.
-// Next tag : 6
-message ChunksMetadata {
-    // Cipher algorithm with which the chunk listing and chunks are encrypted.
-    optional CipherType cipher_type = 1;
-
-    // Defines the type of chunk order this metadata contains. If unspecified this backup file was
-    // created before INLINE_LENGTHS was supported, thus assume it is EXPLICIT_STARTS.
-    optional ChunkOrderingType chunk_ordering_type = 5
-    [default = CHUNK_ORDERING_TYPE_UNSPECIFIED];
-
-    // Encrypted bytes of ChunkOrdering
-    optional bytes chunk_ordering = 2;
-
-    // The type of algorithm used for the checksum of the plaintext. (See ChunkOrdering.) This is
-    // for forwards compatibility in case we change the algorithm in the future. For now, always
-    // SHA-256.
-    optional ChecksumType checksum_type = 3;
-
-    // This used to be the plaintext tertiary key. No longer used.
-    reserved 4;
-}
\ No newline at end of file
diff --git a/packages/BackupEncryption/proto/key_value_listing.proto b/packages/BackupEncryption/proto/key_value_listing.proto
deleted file mode 100644
index 001e697..0000000
--- a/packages/BackupEncryption/proto/key_value_listing.proto
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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
- */
-
-syntax = "proto2";
-
-package android_backup_crypto;
-
-option java_package = "com.android.server.backup.encryption.protos";
-option java_outer_classname = "KeyValueListingProto";
-
-// An entry of a key-value pair.
-message KeyValueEntry {
-  // Plaintext key of the key-value pair.
-  optional string key = 1;
-  // SHA-256 MAC of the plaintext of the chunk containing the pair
-  optional bytes hash = 2;
-}
-
-// Describes the key/value pairs currently in the backup blob, mapping from the
-// plaintext key to the hash of the chunk containing the pair.
-//
-// This is local state stored on the device. It is never sent to the
-// backup server. See ChunkOrdering for how the device restores the
-// key-value pairs in the correct order.
-message KeyValueListing {
-  repeated KeyValueEntry entries = 1;
-}
diff --git a/packages/BackupEncryption/proto/key_value_pair.proto b/packages/BackupEncryption/proto/key_value_pair.proto
deleted file mode 100644
index 177fa30..0000000
--- a/packages/BackupEncryption/proto/key_value_pair.proto
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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
- */
-
-syntax = "proto2";
-
-package android_backup_crypto;
-
-option java_package = "com.android.server.backup.encryption.protos";
-option java_outer_classname = "KeyValuePairProto";
-
-// Serialized form of a key-value pair, when it is to be encrypted in a blob.
-// The backup blob for a key-value database consists of repeated encrypted
-// key-value pairs like this, in a randomized order. See ChunkOrdering for how
-// these are then reconstructed during a restore.
-message KeyValuePair {
-  optional string key = 1;
-  optional bytes value = 2;
-}
diff --git a/packages/BackupEncryption/proto/wrapped_key.proto b/packages/BackupEncryption/proto/wrapped_key.proto
deleted file mode 100644
index 817b7b40..0000000
--- a/packages/BackupEncryption/proto/wrapped_key.proto
+++ /dev/null
@@ -1,52 +0,0 @@
-syntax = "proto2";
-
-package android_backup_crypto;
-
-option java_package = "com.android.server.backup.encryption.protos";
-option java_outer_classname = "WrappedKeyProto";
-
-// Metadata associated with a tertiary key.
-message KeyMetadata {
-  // Type of Cipher algorithm the key is used for.
-  enum Type {
-    UNKNOWN = 0;
-    // No padding. Uses 12-byte nonce. Tag length 16 bytes.
-    AES_256_GCM = 1;
-  }
-
-  // What kind of Cipher algorithm the key is used for. We assume at the moment
-  // that this will always be AES_256_GCM and throw if this is not the case.
-  // Provided here for forwards compatibility in case at some point we need to
-  // change Cipher algorithm.
-  optional Type type = 1;
-}
-
-// An encrypted tertiary key.
-message WrappedKey {
-  // The Cipher with which the key was encrypted.
-  enum WrapAlgorithm {
-    UNKNOWN = 0;
-    // No padding. Uses 16-byte nonce (see nonce field). Tag length 16 bytes.
-    // The nonce is 16-bytes as this is wrapped with a key in AndroidKeyStore.
-    // AndroidKeyStore requires that it generates the IV, and it generates a
-    // 16-byte IV for you. You CANNOT provide your own IV.
-    AES_256_GCM = 1;
-  }
-
-  // Cipher algorithm used to wrap the key. We assume at the moment that this
-  // is always AES_256_GC and throw if this is not the case. Provided here for
-  // forwards compatibility if at some point we need to change Cipher algorithm.
-  optional WrapAlgorithm wrap_algorithm = 1;
-
-  // The nonce used to initialize the Cipher in AES/256/GCM mode.
-  optional bytes nonce = 2;
-
-  // The encrypted bytes of the key material.
-  optional bytes key = 3;
-
-  // Associated key metadata.
-  optional KeyMetadata metadata = 4;
-
-  // Deprecated field; Do not use
-  reserved 5;
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
deleted file mode 100644
index bb1336f..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * 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.encryption;
-
-import static com.android.internal.util.Preconditions.checkState;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.RecoveryController;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.security.KeyStoreException;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-
-/**
- * State about encrypted backups that needs to be remembered.
- */
-public class CryptoSettings {
-
-    private static final String TAG = "CryptoSettings";
-
-    private static final String SHARED_PREFERENCES_NAME = "crypto_settings";
-
-    private static final String KEY_IS_INITIALIZED = "isInitialized";
-    private static final String KEY_ACTIVE_SECONDARY_ALIAS = "activeSecondary";
-    private static final String KEY_NEXT_SECONDARY_ALIAS = "nextSecondary";
-    private static final String SECONDARY_KEY_LAST_ROTATED_AT = "secondaryKeyLastRotatedAt";
-    private static final String[] SETTINGS_FOR_BACKUP = {
-        KEY_IS_INITIALIZED,
-        KEY_ACTIVE_SECONDARY_ALIAS,
-        KEY_NEXT_SECONDARY_ALIAS,
-        SECONDARY_KEY_LAST_ROTATED_AT
-    };
-
-    private static final long DEFAULT_SECONDARY_KEY_ROTATION_PERIOD =
-            TimeUnit.MILLISECONDS.convert(31, TimeUnit.DAYS);
-
-    private static final String KEY_ANCESTRAL_SECONDARY_KEY_VERSION =
-            "ancestral_secondary_key_version";
-
-    private final SharedPreferences mSharedPreferences;
-    private final Context mContext;
-
-    /**
-     * A new instance.
-     *
-     * @param context For looking up the {@link SharedPreferences}, for storing state.
-     * @return The instance.
-     */
-    public static CryptoSettings getInstance(Context context) {
-        // We need single process mode because CryptoSettings may be used from several processes
-        // simultaneously.
-        SharedPreferences sharedPreferences =
-                context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
-        return new CryptoSettings(sharedPreferences, context);
-    }
-
-    /**
-     * A new instance using {@link SharedPreferences} in the default mode.
-     *
-     * <p>This will not work across multiple processes but will work in tests.
-     */
-    @VisibleForTesting
-    public static CryptoSettings getInstanceForTesting(Context context) {
-        SharedPreferences sharedPreferences =
-                context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
-        return new CryptoSettings(sharedPreferences, context);
-    }
-
-    private CryptoSettings(SharedPreferences sharedPreferences, Context context) {
-        mSharedPreferences = Objects.requireNonNull(sharedPreferences);
-        mContext = Objects.requireNonNull(context);
-    }
-
-    /**
-     * The alias of the current active secondary key. This should be used to retrieve the key from
-     * AndroidKeyStore.
-     */
-    public Optional<String> getActiveSecondaryKeyAlias() {
-        return getStringInSharedPrefs(KEY_ACTIVE_SECONDARY_ALIAS);
-    }
-
-    /**
-     * The alias of the secondary key to which the client is rotating. The rotation is not
-     * immediate, which is why this setting is needed. Once the next key is created, it can take up
-     * to 72 hours potentially (or longer if the user has no network) for the next key to be synced
-     * with the keystore. Only after that has happened does the client attempt to re-wrap all
-     * tertiary keys and commit the rotation.
-     */
-    public Optional<String> getNextSecondaryKeyAlias() {
-        return getStringInSharedPrefs(KEY_NEXT_SECONDARY_ALIAS);
-    }
-
-    /**
-     * If the settings have been initialized.
-     */
-    public boolean getIsInitialized() {
-        return mSharedPreferences.getBoolean(KEY_IS_INITIALIZED, false);
-    }
-
-    /**
-     * Sets the alias of the currently active secondary key.
-     *
-     * @param activeAlias The alias, as in AndroidKeyStore.
-     * @throws IllegalArgumentException if the alias is not in the user's keystore.
-     */
-    public void setActiveSecondaryKeyAlias(String activeAlias) throws IllegalArgumentException {
-        assertIsValidAlias(activeAlias);
-        mSharedPreferences.edit().putString(KEY_ACTIVE_SECONDARY_ALIAS, activeAlias).apply();
-    }
-
-    /**
-     * Sets the alias of the secondary key to which the client is rotating.
-     *
-     * @param nextAlias The alias, as in AndroidKeyStore.
-     * @throws KeyStoreException if unable to check whether alias is valid in the keystore.
-     * @throws IllegalArgumentException if the alias is not in the user's keystore.
-     */
-    public void setNextSecondaryAlias(String nextAlias) throws IllegalArgumentException {
-        assertIsValidAlias(nextAlias);
-        mSharedPreferences.edit().putString(KEY_NEXT_SECONDARY_ALIAS, nextAlias).apply();
-    }
-
-    /**
-     * Unsets the alias of the key to which the client is rotating. This is generally performed once
-     * a rotation is complete.
-     */
-    public void removeNextSecondaryKeyAlias() {
-        mSharedPreferences.edit().remove(KEY_NEXT_SECONDARY_ALIAS).apply();
-    }
-
-    /**
-     * Sets the timestamp of when the secondary key was last rotated.
-     *
-     * @param timestamp The timestamp to set.
-     */
-    public void setSecondaryLastRotated(long timestamp) {
-        mSharedPreferences.edit().putLong(SECONDARY_KEY_LAST_ROTATED_AT, timestamp).apply();
-    }
-
-    /**
-     * Returns a timestamp of when the secondary key was last rotated.
-     *
-     * @return The timestamp.
-     */
-    public Optional<Long> getSecondaryLastRotated() {
-        if (!mSharedPreferences.contains(SECONDARY_KEY_LAST_ROTATED_AT)) {
-            return Optional.empty();
-        }
-        return Optional.of(mSharedPreferences.getLong(SECONDARY_KEY_LAST_ROTATED_AT, -1));
-    }
-
-    /**
-     * Sets the settings to have been initialized. (Otherwise loading should try to initialize
-     * again.)
-     */
-    private void setIsInitialized() {
-        mSharedPreferences.edit().putBoolean(KEY_IS_INITIALIZED, true).apply();
-    }
-
-    /**
-     * Initializes with the given key alias.
-     *
-     * @param alias The secondary key alias to be set as active.
-     * @throws IllegalArgumentException if the alias does not reference a valid key.
-     * @throws IllegalStateException if attempting to initialize an already initialized settings.
-     */
-    public void initializeWithKeyAlias(String alias) throws IllegalArgumentException {
-        checkState(
-                !getIsInitialized(), "Attempting to initialize an already initialized settings.");
-        setActiveSecondaryKeyAlias(alias);
-        setIsInitialized();
-    }
-
-    /** Returns the secondary key version of the encrypted backup set to restore from (if set). */
-    public Optional<String> getAncestralSecondaryKeyVersion() {
-        return Optional.ofNullable(
-                mSharedPreferences.getString(KEY_ANCESTRAL_SECONDARY_KEY_VERSION, null));
-    }
-
-    /** Sets the secondary key version of the encrypted backup set to restore from. */
-    public void setAncestralSecondaryKeyVersion(String ancestralSecondaryKeyVersion) {
-        mSharedPreferences
-                .edit()
-                .putString(KEY_ANCESTRAL_SECONDARY_KEY_VERSION, ancestralSecondaryKeyVersion)
-                .apply();
-    }
-
-    /** The number of milliseconds between secondary key rotation */
-    public long backupSecondaryKeyRotationIntervalMs() {
-        return DEFAULT_SECONDARY_KEY_ROTATION_PERIOD;
-    }
-
-    /** Deletes all crypto settings related to backup (as opposed to restore). */
-    public void clearAllSettingsForBackup() {
-        Editor sharedPrefsEditor = mSharedPreferences.edit();
-        for (String backupSettingKey : SETTINGS_FOR_BACKUP) {
-            sharedPrefsEditor.remove(backupSettingKey);
-        }
-        sharedPrefsEditor.apply();
-
-        Slog.d(TAG, "Cleared crypto settings for backup");
-    }
-
-    /**
-     * Throws {@link IllegalArgumentException} if the alias does not refer to a key that is in
-     * the {@link RecoveryController}.
-     */
-    private void assertIsValidAlias(String alias) throws IllegalArgumentException {
-        try {
-            if (!RecoveryController.getInstance(mContext).getAliases().contains(alias)) {
-                throw new IllegalArgumentException(alias + " is not in RecoveryController");
-            }
-        } catch (InternalRecoveryServiceException e) {
-            throw new IllegalArgumentException("Problem accessing recovery service", e);
-        }
-    }
-
-    private Optional<String> getStringInSharedPrefs(String key) {
-        return Optional.ofNullable(mSharedPreferences.getString(key, null));
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/EncryptionKeyHelper.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/EncryptionKeyHelper.java
deleted file mode 100644
index 2035b66..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/EncryptionKeyHelper.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.encryption;
-
-import android.content.Context;
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.RecoveryController;
-
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
-import com.android.server.backup.encryption.keys.TertiaryKeyManager;
-import com.android.server.backup.encryption.keys.TertiaryKeyRotationScheduler;
-
-import java.io.IOException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.UnrecoverableKeyException;
-
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-
-class EncryptionKeyHelper {
-    private static SecureRandom sSecureRandom = new  SecureRandom();
-
-    private final Context mContext;
-    private final RecoverableKeyStoreSecondaryKeyManager
-            .RecoverableKeyStoreSecondaryKeyManagerProvider
-            mSecondaryKeyManagerProvider;
-
-    EncryptionKeyHelper(Context context) {
-        mContext = context;
-        mSecondaryKeyManagerProvider =
-                () ->
-                        new RecoverableKeyStoreSecondaryKeyManager(
-                                RecoveryController.getInstance(mContext), sSecureRandom);
-    }
-
-    RecoverableKeyStoreSecondaryKeyManager
-            .RecoverableKeyStoreSecondaryKeyManagerProvider getKeyManagerProvider() {
-        return mSecondaryKeyManagerProvider;
-    }
-
-    RecoverableKeyStoreSecondaryKey getActiveSecondaryKey()
-            throws UnrecoverableKeyException, InternalRecoveryServiceException {
-        String keyAlias = CryptoSettings.getInstance(mContext).getActiveSecondaryKeyAlias().get();
-        return mSecondaryKeyManagerProvider.get().get(keyAlias).get();
-    }
-
-    SecretKey getTertiaryKey(
-            String packageName,
-            RecoverableKeyStoreSecondaryKey secondaryKey)
-            throws IllegalBlockSizeException, InvalidAlgorithmParameterException,
-            NoSuchAlgorithmException, IOException, NoSuchPaddingException,
-            InvalidKeyException {
-        TertiaryKeyManager tertiaryKeyManager =
-                new TertiaryKeyManager(
-                        mContext,
-                        sSecureRandom,
-                        TertiaryKeyRotationScheduler.getInstance(mContext),
-                        secondaryKey,
-                        packageName);
-        return tertiaryKeyManager.getKey();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullBackupDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullBackupDataProcessor.java
deleted file mode 100644
index f3ab2bde..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullBackupDataProcessor.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.encryption;
-
-import android.app.backup.BackupTransport;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/** Accepts the full backup data stream and sends it to the server. */
-public interface FullBackupDataProcessor {
-    /**
-     * Prepares the upload.
-     *
-     * <p>After this, call {@link #start()} to establish the connection.
-     *
-     * @param inputStream to read the backup data from, calling {@link #finish} or {@link #cancel}
-     *     will close the stream
-     * @return {@code true} if the connection was set up successfully, otherwise {@code false}
-     */
-    boolean initiate(InputStream inputStream) throws IOException;
-
-    /**
-     * Starts the upload, establishing the connection to the server.
-     *
-     * <p>After this, call {@link #pushData(int)} to request that the processor reads data from the
-     * socket, and uploads it to the server.
-     *
-     * <p>After this you must call one of {@link #cancel()}, {@link #finish()}, {@link
-     * #handleCheckSizeRejectionZeroBytes()}, {@link #handleCheckSizeRejectionQuotaExceeded()} or
-     * {@link #handleSendBytesQuotaExceeded()} to close the upload.
-     */
-    void start();
-
-    /**
-     * Requests that the processor read {@code numBytes} from the input stream passed in {@link
-     * #initiate(InputStream)} and upload them to the server.
-     *
-     * @return {@link BackupTransport#TRANSPORT_OK} if the upload succeeds, or {@link
-     *     BackupTransport#TRANSPORT_QUOTA_EXCEEDED} if the upload exceeded the server-side app size
-     *     quota, or {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} for other errors.
-     */
-    int pushData(int numBytes);
-
-    /** Cancels the upload and tears down the connection. */
-    void cancel();
-
-    /**
-     * Finish the upload and tear down the connection.
-     *
-     * <p>Call this after there is no more data to push with {@link #pushData(int)}.
-     *
-     * @return One of {@link BackupTransport#TRANSPORT_OK} if the app upload succeeds, {@link
-     *     BackupTransport#TRANSPORT_QUOTA_EXCEEDED} if the upload exceeded the server-side app size
-     *     quota, {@link BackupTransport#TRANSPORT_ERROR} for server 500s, or {@link
-     *     BackupTransport#TRANSPORT_PACKAGE_REJECTED} for other errors.
-     */
-    int finish();
-
-    /**
-     * Notifies the processor that the current upload should be terminated because the estimated
-     * size is zero.
-     */
-    void handleCheckSizeRejectionZeroBytes();
-
-    /**
-     * Notifies the processor that the current upload should be terminated because the estimated
-     * size exceeds the quota.
-     */
-    void handleCheckSizeRejectionQuotaExceeded();
-
-    /**
-     * Notifies this class that the current upload should be terminated because the quota was
-     * exceeded during upload.
-     */
-    void handleSendBytesQuotaExceeded();
-
-    /**
-     * Attaches {@link FullBackupCallbacks} which the processor will notify when the backup
-     * succeeds.
-     */
-    void attachCallbacks(FullBackupCallbacks fullBackupCallbacks);
-
-    /**
-     * Implemented by the caller of the processor to receive notification of when the backup
-     * succeeds.
-     */
-    interface FullBackupCallbacks {
-        /** The processor calls this to indicate that the current backup has succeeded. */
-        void onSuccess();
-
-        /** The processor calls this if the upload failed for a non-transient reason. */
-        void onTransferFailed();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDataProcessor.java
deleted file mode 100644
index e4c4049..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDataProcessor.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.encryption;
-
-import java.io.IOException;
-
-/**
- * Retrieves the data during a full restore, decrypting it if necessary.
- *
- * <p>Use {@link FullRestoreDataProcessorFactory} to construct the encrypted or unencrypted
- * processor as appropriate during restore.
- */
-public interface FullRestoreDataProcessor {
-    /** Return value of {@link #readNextChunk} when there is no more data to download. */
-    int END_OF_STREAM = -1;
-
-    /**
-     * Reads the next chunk of restore data and writes it to the given buffer.
-     *
-     * <p>Where necessary, will open the connection to the server and/or decrypt the backup file.
-     *
-     * <p>The implementation may retry various errors. If the retries fail it will throw the
-     * relevant exception.
-     *
-     * @return the number of bytes read, or {@link #END_OF_STREAM} if there is no more data
-     * @throws IOException when downloading from the network or writing to disk
-     */
-    int readNextChunk(byte[] buffer) throws IOException;
-
-    /**
-     * Closes the connection to the server, deletes any temporary files and optionally sends a log
-     * with the given finish type.
-     *
-     * @param finishType one of {@link FullRestoreDownloader.FinishType}
-     */
-    void finish(FullRestoreDownloader.FinishType finishType);
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDownloader.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDownloader.java
deleted file mode 100644
index afcca79..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDownloader.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.encryption;
-
-import java.io.IOException;
-
-/** Interface for classes which will provide backup data */
-public abstract class FullRestoreDownloader {
-    /** Enum to provide information on why a download finished */
-    public enum FinishType {
-        UNKNOWN_FINISH(0),
-        // Finish the downloading and successfully write data to Android OS.
-        FINISHED(1),
-        // Download failed with any kind of exception.
-        TRANSFER_FAILURE(2),
-        // Download failed due to auth failure on the device.
-        AUTH_FAILURE(3),
-        // Aborted by Android Framework.
-        FRAMEWORK_ABORTED(4);
-
-        private int mValue;
-
-        FinishType(int value) {
-            mValue = value;
-        }
-    }
-
-    /** Get the next data chunk from the backing store */
-    public abstract int readNextChunk(byte[] buffer) throws IOException;
-
-    /** Called when we've finished restoring the data */
-    public abstract void finish(FinishType finishType);
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java
deleted file mode 100644
index db2dd2f..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.encryption;
-
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.keys.KeyWrapUtils;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-import com.android.server.backup.encryption.tasks.EncryptedKvBackupTask;
-import com.android.server.backup.encryption.tasks.EncryptedKvRestoreTask;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.SecureRandom;
-import java.util.Map;
-
-public class KeyValueEncrypter {
-    private static final String TAG = "KeyValueEncrypter";
-
-    private final Context mContext;
-    private final EncryptionKeyHelper mKeyHelper;
-
-    public KeyValueEncrypter(Context context) {
-        mContext = context;
-        mKeyHelper = new EncryptionKeyHelper(mContext);
-    }
-
-    public void encryptKeyValueData(
-            String packageName, ParcelFileDescriptor inputFd, OutputStream outputStream)
-            throws Exception {
-        EncryptedKvBackupTask.EncryptedKvBackupTaskFactory backupTaskFactory =
-                new EncryptedKvBackupTask.EncryptedKvBackupTaskFactory();
-        EncryptedKvBackupTask backupTask =
-                backupTaskFactory.newInstance(
-                        mContext,
-                        new SecureRandom(),
-                        new FileBackupServer(outputStream),
-                        CryptoSettings.getInstance(mContext),
-                        mKeyHelper.getKeyManagerProvider(),
-                        inputFd,
-                        packageName);
-        backupTask.performBackup(/* incremental */ false);
-    }
-
-    public void decryptKeyValueData(String packageName,
-            InputStream encryptedInputStream, ParcelFileDescriptor outputFd) throws Exception {
-        RecoverableKeyStoreSecondaryKey secondaryKey = mKeyHelper.getActiveSecondaryKey();
-
-        EncryptedKvRestoreTask.EncryptedKvRestoreTaskFactory restoreTaskFactory =
-                new EncryptedKvRestoreTask.EncryptedKvRestoreTaskFactory();
-        EncryptedKvRestoreTask restoreTask =
-                restoreTaskFactory.newInstance(
-                        mContext,
-                        mKeyHelper.getKeyManagerProvider(),
-                        new InputStreamFullRestoreDownloader(encryptedInputStream),
-                        secondaryKey.getAlias(),
-                        KeyWrapUtils.wrap(
-                                secondaryKey.getSecretKey(),
-                                mKeyHelper.getTertiaryKey(packageName, secondaryKey)));
-
-        restoreTask.getRestoreData(outputFd);
-    }
-
-    // TODO(b/142455725): Extract into a commong class.
-    private static class FileBackupServer implements CryptoBackupServer {
-        private static final String EMPTY_DOC_ID = "";
-
-        private final OutputStream mOutputStream;
-
-        FileBackupServer(OutputStream outputStream) {
-            mOutputStream = outputStream;
-        }
-
-        @Override
-        public String uploadIncrementalBackup(
-                String packageName,
-                String oldDocId,
-                byte[] diffScript,
-                WrappedKeyProto.WrappedKey tertiaryKey) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public String uploadNonIncrementalBackup(
-                String packageName, byte[] data, WrappedKeyProto.WrappedKey tertiaryKey) {
-            try {
-                mOutputStream.write(data);
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to write encrypted data to file: ", e);
-            }
-
-            return EMPTY_DOC_ID;
-        }
-
-        @Override
-        public void setActiveSecondaryKeyAlias(
-                String keyAlias, Map<String, WrappedKeyProto.WrappedKey> tertiaryKeys) {
-            // Do nothing.
-        }
-    }
-
-    // TODO(b/142455725): Extract into a commong class.
-    private static class InputStreamFullRestoreDownloader extends FullRestoreDownloader {
-        private final InputStream mInputStream;
-
-        InputStreamFullRestoreDownloader(InputStream inputStream) {
-            mInputStream = inputStream;
-        }
-
-        @Override
-        public int readNextChunk(byte[] buffer) throws IOException {
-            return mInputStream.read(buffer);
-        }
-
-        @Override
-        public void finish(FinishType finishType) {
-            try {
-                mInputStream.close();
-            } catch (IOException e) {
-                Log.w(TAG, "Error while reading restore data");
-            }
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java
deleted file mode 100644
index 66be25b..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.encryption;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/** Utility methods for dealing with Streams */
-public class StreamUtils {
-    private static final int MAX_COPY_BUFFER_SIZE = 1024; // 1k copy buffer size.
-
-    /**
-     * Close a Closeable and silently ignore any IOExceptions.
-     *
-     * @param closeable The closeable to close
-     */
-    public static void closeQuietly(Closeable closeable) {
-        try {
-            closeable.close();
-        } catch (IOException ioe) {
-            // Silently ignore
-        }
-    }
-
-    /**
-     * Copy data from an InputStream to an OutputStream upto a given number of bytes.
-     *
-     * @param in The source InputStream
-     * @param out The destination OutputStream
-     * @param limit The maximum number of bytes to copy
-     * @throws IOException Thrown if there is a problem performing the copy.
-     */
-    public static void copyStream(InputStream in, OutputStream out, int limit) throws IOException {
-        int bufferSize = Math.min(MAX_COPY_BUFFER_SIZE, limit);
-        byte[] buffer = new byte[bufferSize];
-
-        int copied = 0;
-        while (copied < limit) {
-            int maxReadSize = Math.min(bufferSize, limit - copied);
-            int read = in.read(buffer, 0, maxReadSize);
-            if (read < 0) {
-                return; // Reached the stream end before the limit
-            }
-            out.write(buffer, 0, read);
-            copied += read;
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkHash.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkHash.java
deleted file mode 100644
index 1630eb8..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkHash.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunk;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.Arrays;
-import java.util.Base64;
-
-/**
- * Represents the SHA-256 hash of the plaintext of a chunk, which is frequently used as a key.
- *
- * <p>This class is {@link Comparable} and implements {@link #equals(Object)} and {@link
- * #hashCode()}.
- */
-public class ChunkHash implements Comparable<ChunkHash> {
-    /** The length of the hash in bytes. The hash is a SHA-256, so this is 256 bits. */
-    public static final int HASH_LENGTH_BYTES = 256 / 8;
-
-    private static final int UNSIGNED_MASK = 0xFF;
-
-    private final byte[] mHash;
-
-    /** Constructs a new instance which wraps the given SHA-256 hash bytes. */
-    public ChunkHash(byte[] hash) {
-        Preconditions.checkArgument(hash.length == HASH_LENGTH_BYTES, "Hash must have 256 bits");
-        mHash = hash;
-    }
-
-    public byte[] getHash() {
-        return mHash;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof ChunkHash)) {
-            return false;
-        }
-
-        ChunkHash chunkHash = (ChunkHash) o;
-        return Arrays.equals(mHash, chunkHash.mHash);
-    }
-
-    @Override
-    public int hashCode() {
-        return Arrays.hashCode(mHash);
-    }
-
-    @Override
-    public int compareTo(ChunkHash other) {
-        return lexicographicalCompareUnsignedBytes(getHash(), other.getHash());
-    }
-
-    @Override
-    public String toString() {
-        return Base64.getEncoder().encodeToString(mHash);
-    }
-
-    private static int lexicographicalCompareUnsignedBytes(byte[] left, byte[] right) {
-        int minLength = Math.min(left.length, right.length);
-        for (int i = 0; i < minLength; i++) {
-            int result = toInt(left[i]) - toInt(right[i]);
-            if (result != 0) {
-                return result;
-            }
-        }
-        return left.length - right.length;
-    }
-
-    private static int toInt(byte value) {
-        return value & UNSIGNED_MASK;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java
deleted file mode 100644
index 51d7d53..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.encryption.chunk;
-
-import android.annotation.Nullable;
-
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Chunk listing in a format optimized for quick look up of chunks via their hash keys. This is
- * useful when building an incremental backup. After a chunk has been produced, the algorithm can
- * quickly look up whether the chunk existed in the previous backup by checking this chunk listing.
- * It can then tell the server to use that chunk, through telling it the position and length of the
- * chunk in the previous backup's blob.
- */
-public class ChunkListingMap {
-
-    private final Map<ChunkHash, Entry> mChunksByHash;
-
-    /** Construct a map from a {@link ChunksMetadataProto.ChunkListing} protobuf */
-    public static ChunkListingMap fromProto(ChunksMetadataProto.ChunkListing chunkListingProto) {
-        Map<ChunkHash, Entry> entries = new HashMap<>();
-
-        long start = 0;
-
-        for (ChunksMetadataProto.Chunk chunk : chunkListingProto.chunks) {
-            entries.put(new ChunkHash(chunk.hash), new Entry(start, chunk.length));
-            start += chunk.length;
-        }
-
-        return new ChunkListingMap(entries);
-    }
-
-    private ChunkListingMap(Map<ChunkHash, Entry> chunksByHash) {
-        // This is only called from the {@link #fromProto} method, so we don't
-        // need to take a copy.
-        this.mChunksByHash = chunksByHash;
-    }
-
-    /** Returns {@code true} if there is a chunk with the given SHA-256 MAC key in the listing. */
-    public boolean hasChunk(ChunkHash hash) {
-        return mChunksByHash.containsKey(hash);
-    }
-
-    /**
-     * Returns the entry for the chunk with the given hash.
-     *
-     * @param hash The SHA-256 MAC of the plaintext of the chunk.
-     * @return The entry, containing position and length of the chunk in the backup blob, or null if
-     *     it does not exist.
-     */
-    @Nullable
-    public Entry getChunkEntry(ChunkHash hash) {
-        return mChunksByHash.get(hash);
-    }
-
-    /** Information about a chunk entry in a backup blob - i.e., its position and length. */
-    public static final class Entry {
-        private final int mLength;
-        private final long mStart;
-
-        private Entry(long start, int length) {
-            mLength = length;
-            mStart = start;
-        }
-
-        /** Returns the length of the chunk in bytes. */
-        public int getLength() {
-            return mLength;
-        }
-
-        /** Returns the start position of the chunk in the backup blob, in bytes. */
-        public long getStart() {
-            return mStart;
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
deleted file mode 100644
index 9cda339..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunk;
-
-import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
-import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.EXPLICIT_STARTS;
-import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.INLINE_LENGTHS;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** IntDef corresponding to the ChunkOrderingType enum in the ChunksMetadataProto protobuf. */
-@IntDef({CHUNK_ORDERING_TYPE_UNSPECIFIED, EXPLICIT_STARTS, INLINE_LENGTHS})
-@Retention(RetentionPolicy.SOURCE)
-public @interface ChunkOrderingType {}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java
deleted file mode 100644
index edf1b9a..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunk;
-
-import java.util.Arrays;
-
-/**
- * Holds the bytes of an encrypted {@link ChunksMetadataProto.ChunkOrdering}.
- *
- * <p>TODO(b/116575321): After all code is ported, remove the factory method and rename
- * encryptedChunkOrdering() to getBytes().
- */
-public class EncryptedChunkOrdering {
-    /**
-     * Constructs a new object holding the given bytes of an encrypted {@link
-     * ChunksMetadataProto.ChunkOrdering}.
-     *
-     * <p>Note that this just holds an ordering which is already encrypted, it does not encrypt the
-     * ordering.
-     */
-    public static EncryptedChunkOrdering create(byte[] encryptedChunkOrdering) {
-        return new EncryptedChunkOrdering(encryptedChunkOrdering);
-    }
-
-    private final byte[] mEncryptedChunkOrdering;
-
-    /** Get the encrypted chunk ordering */
-    public byte[] encryptedChunkOrdering() {
-        return mEncryptedChunkOrdering;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof EncryptedChunkOrdering)) {
-            return false;
-        }
-
-        EncryptedChunkOrdering encryptedChunkOrdering = (EncryptedChunkOrdering) o;
-        return Arrays.equals(
-                mEncryptedChunkOrdering, encryptedChunkOrdering.mEncryptedChunkOrdering);
-    }
-
-    @Override
-    public int hashCode() {
-        return Arrays.hashCode(mEncryptedChunkOrdering);
-    }
-
-    private EncryptedChunkOrdering(byte[] encryptedChunkOrdering) {
-        mEncryptedChunkOrdering = encryptedChunkOrdering;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java
deleted file mode 100644
index 4010bfd..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * 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.encryption.chunking;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkState;
-
-import android.annotation.Nullable;
-import android.util.Slog;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunk.ChunkListingMap;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Writes batches of {@link EncryptedChunk} to a diff script, and generates the associated {@link
- * ChunksMetadataProto.ChunkListing} and {@link ChunksMetadataProto.ChunkOrdering}.
- */
-public class BackupFileBuilder {
-    private static final String TAG = "BackupFileBuilder";
-
-    private static final int BYTES_PER_KILOBYTE = 1024;
-
-    private final BackupWriter mBackupWriter;
-    private final EncryptedChunkEncoder mEncryptedChunkEncoder;
-    private final ChunkListingMap mOldChunkListing;
-    private final ChunksMetadataProto.ChunkListing mNewChunkListing;
-    private final ChunksMetadataProto.ChunkOrdering mChunkOrdering;
-    private final List<ChunksMetadataProto.Chunk> mKnownChunks = new ArrayList<>();
-    private final List<Integer> mKnownStarts = new ArrayList<>();
-    private final Map<ChunkHash, Long> mChunkStartPositions;
-
-    private long mNewChunksSizeBytes;
-    private boolean mFinished;
-
-    /**
-     * Constructs a new instance which writes raw data to the given {@link OutputStream}, without
-     * generating a diff.
-     *
-     * <p>This class never closes the output stream.
-     */
-    public static BackupFileBuilder createForNonIncremental(OutputStream outputStream) {
-        return new BackupFileBuilder(
-                new RawBackupWriter(outputStream), new ChunksMetadataProto.ChunkListing());
-    }
-
-    /**
-     * Constructs a new instance which writes a diff script to the given {@link OutputStream} using
-     * a {@link SingleStreamDiffScriptWriter}.
-     *
-     * <p>This class never closes the output stream.
-     *
-     * @param oldChunkListing against which the diff will be generated.
-     */
-    public static BackupFileBuilder createForIncremental(
-            OutputStream outputStream, ChunksMetadataProto.ChunkListing oldChunkListing) {
-        return new BackupFileBuilder(
-                DiffScriptBackupWriter.newInstance(outputStream), oldChunkListing);
-    }
-
-    private BackupFileBuilder(
-            BackupWriter backupWriter, ChunksMetadataProto.ChunkListing oldChunkListing) {
-        this.mBackupWriter = backupWriter;
-        // TODO(b/77188289): Use InlineLengthsEncryptedChunkEncoder for key-value backups
-        this.mEncryptedChunkEncoder = new LengthlessEncryptedChunkEncoder();
-        this.mOldChunkListing = ChunkListingMap.fromProto(oldChunkListing);
-
-        mNewChunkListing = new ChunksMetadataProto.ChunkListing();
-        mNewChunkListing.cipherType = ChunksMetadataProto.AES_256_GCM;
-        mNewChunkListing.chunkOrderingType = ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
-
-        mChunkOrdering = new ChunksMetadataProto.ChunkOrdering();
-        mChunkStartPositions = new HashMap<>();
-    }
-
-    /**
-     * Writes the given chunks to the output stream, and adds them to the new chunk listing and
-     * chunk ordering.
-     *
-     * <p>Sorts the chunks in lexicographical order before writing.
-     *
-     * @param allChunks The hashes of all the chunks, in the order they appear in the plaintext.
-     * @param newChunks A map from hash to {@link EncryptedChunk} containing the new chunks not
-     *     present in the previous backup.
-     */
-    public void writeChunks(List<ChunkHash> allChunks, Map<ChunkHash, EncryptedChunk> newChunks)
-            throws IOException {
-        checkState(!mFinished, "Cannot write chunks after flushing.");
-
-        List<ChunkHash> sortedChunks = new ArrayList<>(allChunks);
-        Collections.sort(sortedChunks);
-        for (ChunkHash chunkHash : sortedChunks) {
-            // As we have already included this chunk in the backup file, don't add it again to
-            // deduplicate identical chunks.
-            if (!mChunkStartPositions.containsKey(chunkHash)) {
-                // getBytesWritten() gives us the start of the chunk.
-                mChunkStartPositions.put(chunkHash, mBackupWriter.getBytesWritten());
-
-                writeChunkToFileAndListing(chunkHash, newChunks);
-            }
-        }
-
-        long totalSizeKb = mBackupWriter.getBytesWritten() / BYTES_PER_KILOBYTE;
-        long newChunksSizeKb = mNewChunksSizeBytes / BYTES_PER_KILOBYTE;
-        Slog.d(
-                TAG,
-                "Total backup size: "
-                        + totalSizeKb
-                        + " kb, new chunks size: "
-                        + newChunksSizeKb
-                        + " kb");
-
-        for (ChunkHash chunkHash : allChunks) {
-            mKnownStarts.add(mChunkStartPositions.get(chunkHash).intValue());
-        }
-    }
-
-    /**
-     * Returns a new listing for all of the chunks written so far, setting the given fingerprint
-     * mixer salt (this overrides the {@link ChunksMetadataProto.ChunkListing#fingerprintMixerSalt}
-     * in the old {@link ChunksMetadataProto.ChunkListing} passed into the
-     * {@link #BackupFileBuilder).
-     */
-    public ChunksMetadataProto.ChunkListing getNewChunkListing(
-            @Nullable byte[] fingerprintMixerSalt) {
-        // TODO: b/141537803 Add check to ensure this is called only once per instance
-        mNewChunkListing.fingerprintMixerSalt =
-                fingerprintMixerSalt != null
-                        ? Arrays.copyOf(fingerprintMixerSalt, fingerprintMixerSalt.length)
-                        : new byte[0];
-        mNewChunkListing.chunks = mKnownChunks.toArray(new ChunksMetadataProto.Chunk[0]);
-        return mNewChunkListing;
-    }
-
-    /** Returns a new ordering for all of the chunks written so far, setting the given checksum. */
-    public ChunksMetadataProto.ChunkOrdering getNewChunkOrdering(byte[] checksum) {
-        // TODO: b/141537803 Add check to ensure this is called only once per instance
-        mChunkOrdering.starts = new int[mKnownStarts.size()];
-        for (int i = 0; i < mKnownStarts.size(); i++) {
-            mChunkOrdering.starts[i] = mKnownStarts.get(i).intValue();
-        }
-        mChunkOrdering.checksum = Arrays.copyOf(checksum, checksum.length);
-        return mChunkOrdering;
-    }
-
-    /**
-     * Finishes the backup file by writing the chunk metadata and metadata position.
-     *
-     * <p>Once this is called, calling {@link #writeChunks(List, Map)} will throw {@link
-     * IllegalStateException}.
-     */
-    public void finish(ChunksMetadataProto.ChunksMetadata metadata) throws IOException {
-        Objects.requireNonNull(metadata, "Metadata cannot be null");
-
-        long startOfMetadata = mBackupWriter.getBytesWritten();
-        mBackupWriter.writeBytes(ChunksMetadataProto.ChunksMetadata.toByteArray(metadata));
-        mBackupWriter.writeBytes(toByteArray(startOfMetadata));
-
-        mBackupWriter.flush();
-        mFinished = true;
-    }
-
-    /**
-     * Checks if the given chunk hash references an existing chunk or a new chunk, and adds this
-     * chunk to the backup file and new chunk listing.
-     */
-    private void writeChunkToFileAndListing(
-            ChunkHash chunkHash, Map<ChunkHash, EncryptedChunk> newChunks) throws IOException {
-        Objects.requireNonNull(chunkHash, "Hash cannot be null");
-
-        if (mOldChunkListing.hasChunk(chunkHash)) {
-            ChunkListingMap.Entry oldChunk = mOldChunkListing.getChunkEntry(chunkHash);
-            mBackupWriter.writeChunk(oldChunk.getStart(), oldChunk.getLength());
-
-            checkArgument(oldChunk.getLength() >= 0, "Chunk must have zero or positive length");
-            addChunk(chunkHash.getHash(), oldChunk.getLength());
-        } else if (newChunks.containsKey(chunkHash)) {
-            EncryptedChunk newChunk = newChunks.get(chunkHash);
-            mEncryptedChunkEncoder.writeChunkToWriter(mBackupWriter, newChunk);
-            int length = mEncryptedChunkEncoder.getEncodedLengthOfChunk(newChunk);
-            mNewChunksSizeBytes += length;
-
-            checkArgument(length >= 0, "Chunk must have zero or positive length");
-            addChunk(chunkHash.getHash(), length);
-        } else {
-            throw new IllegalArgumentException(
-                    "Chunk did not exist in old chunks or new chunks: " + chunkHash);
-        }
-    }
-
-    private void addChunk(byte[] chunkHash, int length) {
-        ChunksMetadataProto.Chunk chunk = new ChunksMetadataProto.Chunk();
-        chunk.hash = Arrays.copyOf(chunkHash, chunkHash.length);
-        chunk.length = length;
-        mKnownChunks.add(chunk);
-    }
-
-    private static byte[] toByteArray(long value) {
-        // Note that this code needs to stay compatible with GWT, which has known
-        // bugs when narrowing byte casts of long values occur.
-        byte[] result = new byte[8];
-        for (int i = 7; i >= 0; i--) {
-            result[i] = (byte) (value & 0xffL);
-            value >>= 8;
-        }
-        return result;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupWriter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupWriter.java
deleted file mode 100644
index baa820c..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupWriter.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import java.io.IOException;
-
-/** Writes backup data either as a diff script or as raw data, determined by the implementation. */
-public interface BackupWriter {
-    /** Writes the given bytes to the output. */
-    void writeBytes(byte[] bytes) throws IOException;
-
-    /**
-     * Writes an existing chunk from the previous backup to the output.
-     *
-     * <p>Note: not all implementations support this method.
-     */
-    void writeChunk(long start, int length) throws IOException;
-
-    /** Returns the number of bytes written, included bytes copied from the old file. */
-    long getBytesWritten();
-
-    /**
-     * Indicates that no more bytes or chunks will be written.
-     *
-     * <p>After calling this, you may not call {@link #writeBytes(byte[])} or {@link
-     * #writeChunk(long, int)}
-     */
-    void flush() throws IOException;
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ByteRange.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ByteRange.java
deleted file mode 100644
index 004d9e3..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ByteRange.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import com.android.internal.util.Preconditions;
-
-/** Representation of a range of bytes to be downloaded. */
-final class ByteRange {
-    private final long mStart;
-    private final long mEnd;
-
-    /** Creates a range of bytes which includes {@code mStart} and {@code mEnd}. */
-    ByteRange(long start, long end) {
-        Preconditions.checkArgument(start >= 0);
-        Preconditions.checkArgument(end >= start);
-        mStart = start;
-        mEnd = end;
-    }
-
-    /** Returns the start of the {@code ByteRange}. The start is included in the range. */
-    long getStart() {
-        return mStart;
-    }
-
-    /** Returns the end of the {@code ByteRange}. The end is included in the range. */
-    long getEnd() {
-        return mEnd;
-    }
-
-    /** Returns the number of bytes included in the {@code ByteRange}. */
-    int getLength() {
-        return (int) (mEnd - mStart + 1);
-    }
-
-    /** Creates a new {@link ByteRange} from {@code mStart} to {@code mEnd + length}. */
-    ByteRange extend(long length) {
-        Preconditions.checkArgument(length > 0);
-        return new ByteRange(mStart, mEnd + length);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof ByteRange)) {
-            return false;
-        }
-
-        ByteRange byteRange = (ByteRange) o;
-        return (mEnd == byteRange.mEnd && mStart == byteRange.mStart);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-        result = 31 * result + (int) (mStart ^ (mStart >>> 32));
-        result = 31 * result + (int) (mEnd ^ (mEnd >>> 32));
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("ByteRange{mStart=%d, mEnd=%d}", mStart, mEnd);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkEncryptor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkEncryptor.java
deleted file mode 100644
index 48abc8c..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkEncryptor.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
-
-/** Encrypts chunks of a file using AES/GCM. */
-public class ChunkEncryptor {
-    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
-    private static final int GCM_NONCE_LENGTH_BYTES = 12;
-    private static final int GCM_TAG_LENGTH_BYTES = 16;
-
-    private final SecretKey mSecretKey;
-    private final SecureRandom mSecureRandom;
-
-    /**
-     * A new instance using {@code mSecretKey} to encrypt chunks and {@code mSecureRandom} to
-     * generate nonces.
-     */
-    public ChunkEncryptor(SecretKey secretKey, SecureRandom secureRandom) {
-        this.mSecretKey = secretKey;
-        this.mSecureRandom = secureRandom;
-    }
-
-    /**
-     * Transforms {@code plaintext} into an {@link EncryptedChunk}.
-     *
-     * @param plaintextHash The hash of the plaintext to encrypt, to attach as the key of the chunk.
-     * @param plaintext Bytes to encrypt.
-     * @throws InvalidKeyException If the given secret key is not a valid AES key for decryption.
-     * @throws IllegalBlockSizeException If the input data cannot be encrypted using
-     *     AES/GCM/NoPadding. This should never be the case.
-     */
-    public EncryptedChunk encrypt(ChunkHash plaintextHash, byte[] plaintext)
-            throws InvalidKeyException, IllegalBlockSizeException {
-        byte[] nonce = generateNonce();
-        Cipher cipher;
-        try {
-            cipher = Cipher.getInstance(CIPHER_ALGORITHM);
-            cipher.init(
-                    Cipher.ENCRYPT_MODE,
-                    mSecretKey,
-                    new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * 8, nonce));
-        } catch (NoSuchAlgorithmException
-                | NoSuchPaddingException
-                | InvalidAlgorithmParameterException e) {
-            // This can not happen - AES/GCM/NoPadding is supported.
-            throw new AssertionError(e);
-        }
-        byte[] encryptedBytes;
-        try {
-            encryptedBytes = cipher.doFinal(plaintext);
-        } catch (BadPaddingException e) {
-            // This can not happen - BadPaddingException can only be thrown in decrypt mode.
-            throw new AssertionError("Impossible: threw BadPaddingException in encrypt mode.");
-        }
-
-        return EncryptedChunk.create(/*key=*/ plaintextHash, nonce, encryptedBytes);
-    }
-
-    private byte[] generateNonce() {
-        byte[] nonce = new byte[GCM_NONCE_LENGTH_BYTES];
-        mSecureRandom.nextBytes(nonce);
-        return nonce;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkHasher.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkHasher.java
deleted file mode 100644
index 02d498c..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkHasher.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-
-import javax.crypto.Mac;
-import javax.crypto.SecretKey;
-
-/** Computes the SHA-256 HMAC of a chunk of bytes. */
-public class ChunkHasher {
-    private static final String MAC_ALGORITHM = "HmacSHA256";
-
-    private final SecretKey mSecretKey;
-
-    /** Constructs a new hasher which computes the HMAC using the given secret key. */
-    public ChunkHasher(SecretKey secretKey) {
-        this.mSecretKey = secretKey;
-    }
-
-    /** Returns the SHA-256 over the given bytes. */
-    public ChunkHash computeHash(byte[] plaintext) throws InvalidKeyException {
-        try {
-            Mac mac = Mac.getInstance(MAC_ALGORITHM);
-            mac.init(mSecretKey);
-            return new ChunkHash(mac.doFinal(plaintext));
-        } catch (NoSuchAlgorithmException e) {
-            // This can not happen - AES/GCM/NoPadding is available as part of the framework.
-            throw new AssertionError(e);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/Chunker.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/Chunker.java
deleted file mode 100644
index c9a6293..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/Chunker.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-
-/** Splits an input stream into chunks, which are to be encrypted separately. */
-public interface Chunker {
-    /**
-     * Splits the input stream into chunks.
-     *
-     * @param inputStream The input stream.
-     * @param chunkConsumer A function that processes each chunk as it is produced.
-     * @throws IOException If there is a problem reading the input stream.
-     * @throws GeneralSecurityException if the consumer function throws an error.
-     */
-    void chunkify(InputStream inputStream, ChunkConsumer chunkConsumer)
-            throws IOException, GeneralSecurityException;
-
-    /** Function that consumes chunks. */
-    interface ChunkConsumer {
-        /**
-         * Invoked for each chunk.
-         *
-         * @param chunk Plaintext bytes of chunk.
-         * @throws GeneralSecurityException if there is an issue encrypting the chunk.
-         */
-        void accept(byte[] chunk) throws GeneralSecurityException;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutput.java
deleted file mode 100644
index ae2e150..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutput.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.encryption.chunking;
-
-import static com.android.internal.util.Preconditions.checkState;
-
-import android.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.backup.encryption.tasks.DecryptedChunkOutput;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/** Writes plaintext chunks to a file, building a digest of the plaintext of the resulting file. */
-public class DecryptedChunkFileOutput implements DecryptedChunkOutput {
-    @VisibleForTesting static final String DIGEST_ALGORITHM = "SHA-256";
-
-    private final File mOutputFile;
-    private final MessageDigest mMessageDigest;
-    @Nullable private FileOutputStream mFileOutputStream;
-    private boolean mClosed;
-    @Nullable private byte[] mDigest;
-
-    /**
-     * Constructs a new instance which writes chunks to the given file and uses the default message
-     * digest algorithm.
-     */
-    public DecryptedChunkFileOutput(File outputFile) {
-        mOutputFile = outputFile;
-        try {
-            mMessageDigest = MessageDigest.getInstance(DIGEST_ALGORITHM);
-        } catch (NoSuchAlgorithmException e) {
-            throw new AssertionError(
-                    "Impossible condition: JCE thinks it does not support AES.", e);
-        }
-    }
-
-    @Override
-    public DecryptedChunkOutput open() throws IOException {
-        checkState(mFileOutputStream == null, "Cannot open twice");
-        mFileOutputStream = new FileOutputStream(mOutputFile);
-        return this;
-    }
-
-    @Override
-    public void processChunk(byte[] plaintextBuffer, int length) throws IOException {
-        checkState(mFileOutputStream != null, "Must open before processing chunks");
-        mFileOutputStream.write(plaintextBuffer, /*off=*/ 0, length);
-        mMessageDigest.update(plaintextBuffer, /*offset=*/ 0, length);
-    }
-
-    @Override
-    public byte[] getDigest() {
-        checkState(mClosed, "Must close before getting mDigest");
-
-        // After the first call to mDigest() the MessageDigest is reset, thus we must store the
-        // result.
-        if (mDigest == null) {
-            mDigest = mMessageDigest.digest();
-        }
-        return mDigest;
-    }
-
-    @Override
-    public void close() throws IOException {
-        mFileOutputStream.close();
-        mClosed = true;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java
deleted file mode 100644
index 69fb5cb..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/** Writes backup data to a diff script, using a {@link SingleStreamDiffScriptWriter}. */
-public class DiffScriptBackupWriter implements BackupWriter {
-    /**
-     * The maximum size of a chunk in the diff script. The diff script writer {@code mWriter} will
-     * buffer this many bytes in memory.
-     */
-    private static final int ENCRYPTION_DIFF_SCRIPT_MAX_CHUNK_SIZE_BYTES = 1024 * 1024;
-
-    private final SingleStreamDiffScriptWriter mWriter;
-    private long mBytesWritten;
-
-    /**
-     * Constructs a new writer which writes the diff script to the given output stream, using the
-     * maximum new chunk size {@code ENCRYPTION_DIFF_SCRIPT_MAX_CHUNK_SIZE_BYTES}.
-     */
-    public static DiffScriptBackupWriter newInstance(OutputStream outputStream) {
-        SingleStreamDiffScriptWriter writer =
-                new SingleStreamDiffScriptWriter(
-                        outputStream, ENCRYPTION_DIFF_SCRIPT_MAX_CHUNK_SIZE_BYTES);
-        return new DiffScriptBackupWriter(writer);
-    }
-
-    @VisibleForTesting
-    DiffScriptBackupWriter(SingleStreamDiffScriptWriter writer) {
-        mWriter = writer;
-    }
-
-    @Override
-    public void writeBytes(byte[] bytes) throws IOException {
-        for (byte b : bytes) {
-            mWriter.writeByte(b);
-        }
-
-        mBytesWritten += bytes.length;
-    }
-
-    @Override
-    public void writeChunk(long start, int length) throws IOException {
-        mWriter.writeChunk(start, length);
-        mBytesWritten += length;
-    }
-
-    @Override
-    public long getBytesWritten() {
-        return mBytesWritten;
-    }
-
-    @Override
-    public void flush() throws IOException {
-        mWriter.flush();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DiffScriptWriter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DiffScriptWriter.java
deleted file mode 100644
index 49d1571..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DiffScriptWriter.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/** Writer that formats a Diff Script and writes it to an output source. */
-interface DiffScriptWriter {
-    /** Adds a new byte to the diff script. */
-    void writeByte(byte b) throws IOException;
-
-    /** Adds a known chunk to the diff script. */
-    void writeChunk(long chunkStart, int chunkLength) throws IOException;
-
-    /** Indicates that no more bytes or chunks will be added to the diff script. */
-    void flush() throws IOException;
-
-    interface Factory {
-        DiffScriptWriter create(OutputStream outputStream);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunk.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunk.java
deleted file mode 100644
index cde59fa..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunk.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import com.android.internal.util.Preconditions;
-import com.android.server.backup.encryption.chunk.ChunkHash;
-
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * A chunk of a file encrypted using AES/GCM.
- *
- * <p>TODO(b/116575321): After all code is ported, remove the factory method and rename
- * encryptedBytes(), key() and nonce().
- */
-public class EncryptedChunk {
-    public static final int KEY_LENGTH_BYTES = ChunkHash.HASH_LENGTH_BYTES;
-    public static final int NONCE_LENGTH_BYTES = 12;
-
-    /**
-     * Constructs a new instance with the given key, nonce, and encrypted bytes.
-     *
-     * @param key SHA-256 Hmac of the chunk plaintext.
-     * @param nonce Nonce with which the bytes of the chunk were encrypted.
-     * @param encryptedBytes Encrypted bytes of the chunk.
-     */
-    public static EncryptedChunk create(ChunkHash key, byte[] nonce, byte[] encryptedBytes) {
-        Preconditions.checkArgument(
-                nonce.length == NONCE_LENGTH_BYTES, "Nonce does not have the correct length.");
-        return new EncryptedChunk(key, nonce, encryptedBytes);
-    }
-
-    private ChunkHash mKey;
-    private byte[] mNonce;
-    private byte[] mEncryptedBytes;
-
-    private EncryptedChunk(ChunkHash key, byte[] nonce, byte[] encryptedBytes) {
-        mKey = key;
-        mNonce = nonce;
-        mEncryptedBytes = encryptedBytes;
-    }
-
-    /** The SHA-256 Hmac of the plaintext bytes of the chunk. */
-    public ChunkHash key() {
-        return mKey;
-    }
-
-    /** The nonce with which the chunk was encrypted. */
-    public byte[] nonce() {
-        return mNonce;
-    }
-
-    /** The encrypted bytes of the chunk. */
-    public byte[] encryptedBytes() {
-        return mEncryptedBytes;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof EncryptedChunk)) {
-            return false;
-        }
-
-        EncryptedChunk encryptedChunkOrdering = (EncryptedChunk) o;
-        return Arrays.equals(mEncryptedBytes, encryptedChunkOrdering.mEncryptedBytes)
-                && Arrays.equals(mNonce, encryptedChunkOrdering.mNonce)
-                && mKey.equals(encryptedChunkOrdering.mKey);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mKey, Arrays.hashCode(mNonce), Arrays.hashCode(mEncryptedBytes));
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
deleted file mode 100644
index 16beda3..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import com.android.server.backup.encryption.chunk.ChunkOrderingType;
-
-import java.io.IOException;
-
-/** Encodes an {@link EncryptedChunk} as bytes to write to the encrypted backup file. */
-public interface EncryptedChunkEncoder {
-    /**
-     * Encodes the given chunk and asks the writer to write it.
-     *
-     * <p>The chunk will be encoded in the format [nonce]+[encrypted data].
-     *
-     * <p>TODO(b/116575321): Choose a more descriptive method name after the code move is done.
-     */
-    void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException;
-
-    /**
-     * Returns the length in bytes that this chunk would be if encoded with {@link
-     * #writeChunkToWriter}.
-     */
-    int getEncodedLengthOfChunk(EncryptedChunk chunk);
-
-    /**
-     * Returns the {@link ChunkOrderingType} that must be included in the backup file, when using
-     * this decoder, so that the file may be correctly decoded.
-     */
-    @ChunkOrderingType
-    int getChunkOrderingType();
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
deleted file mode 100644
index 6b9be9f..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import com.android.server.backup.encryption.chunk.ChunkOrderingType;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-
-import java.io.IOException;
-
-/**
- * Encodes an {@link EncryptedChunk} as bytes, prepending the length of the chunk.
- *
- * <p>This allows us to decode the backup file during restore without any extra information about
- * the boundaries of the chunks. The backup file should contain a chunk ordering in mode {@link
- * ChunksMetadataProto#INLINE_LENGTHS}.
- *
- * <p>We use this implementation during key value backup.
- */
-public class InlineLengthsEncryptedChunkEncoder implements EncryptedChunkEncoder {
-    public static final int BYTES_LENGTH = Integer.SIZE / Byte.SIZE;
-
-    private final LengthlessEncryptedChunkEncoder mLengthlessEncryptedChunkEncoder =
-            new LengthlessEncryptedChunkEncoder();
-
-    @Override
-    public void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException {
-        int length = mLengthlessEncryptedChunkEncoder.getEncodedLengthOfChunk(chunk);
-        writer.writeBytes(toByteArray(length));
-        mLengthlessEncryptedChunkEncoder.writeChunkToWriter(writer, chunk);
-    }
-
-    @Override
-    public int getEncodedLengthOfChunk(EncryptedChunk chunk) {
-        return BYTES_LENGTH + mLengthlessEncryptedChunkEncoder.getEncodedLengthOfChunk(chunk);
-    }
-
-    @Override
-    @ChunkOrderingType
-    public int getChunkOrderingType() {
-        return ChunksMetadataProto.INLINE_LENGTHS;
-    }
-
-    /**
-     * Returns a big-endian representation of {@code value} in a 4-element byte array; equivalent to
-     * {@code ByteBuffer.allocate(4).putInt(value).array()}. For example, the input value {@code
-     * 0x12131415} would yield the byte array {@code {0x12, 0x13, 0x14, 0x15}}.
-     *
-     * <p>Equivalent to guava's Ints.toByteArray.
-     */
-    static byte[] toByteArray(int value) {
-        return new byte[] {
-            (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value
-        };
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
deleted file mode 100644
index e707350..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import com.android.server.backup.encryption.chunk.ChunkOrderingType;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-
-import java.io.IOException;
-
-/**
- * Encodes an {@link EncryptedChunk} as bytes without including any information about the length of
- * the chunk.
- *
- * <p>In order for us to decode the backup file during restore it must include a chunk ordering in
- * mode {@link ChunksMetadataProto#EXPLICIT_STARTS}, which contains the boundaries of the chunks in
- * the encrypted file. This information allows us to decode the backup file and divide it into
- * chunks without including the length of each chunk inline.
- *
- * <p>We use this implementation during full backup.
- */
-public class LengthlessEncryptedChunkEncoder implements EncryptedChunkEncoder {
-    @Override
-    public void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException {
-        writer.writeBytes(chunk.nonce());
-        writer.writeBytes(chunk.encryptedBytes());
-    }
-
-    @Override
-    public int getEncodedLengthOfChunk(EncryptedChunk chunk) {
-        return chunk.nonce().length + chunk.encryptedBytes().length;
-    }
-
-    @Override
-    @ChunkOrderingType
-    public int getChunkOrderingType() {
-        return ChunksMetadataProto.EXPLICIT_STARTS;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
deleted file mode 100644
index 4aea601..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import java.io.OutputStream;
-
-/** An interface that wraps one {@link OutputStream} with another for filtration purposes. */
-public interface OutputStreamWrapper {
-    /** Wraps a given {@link OutputStream}. */
-    OutputStream wrap(OutputStream outputStream);
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java
deleted file mode 100644
index b0a562c..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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.encryption.chunking;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.AtomicFile;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
-
-import com.google.protobuf.nano.MessageNano;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * Stores a nano proto for each package, persisting the proto to disk.
- *
- * <p>This is used to store {@link ChunksMetadataProto.ChunkListing}.
- *
- * @param <T> the type of nano proto to store.
- */
-public class ProtoStore<T extends MessageNano> {
-    private static final String CHUNK_LISTING_FOLDER = "backup_chunk_listings";
-    private static final String KEY_VALUE_LISTING_FOLDER = "backup_kv_listings";
-
-    private static final String TAG = "BupEncProtoStore";
-
-    private final File mStoreFolder;
-    private final Class<T> mClazz;
-
-    /** Creates a new instance which stores chunk listings at the default location. */
-    public static ProtoStore<ChunksMetadataProto.ChunkListing> createChunkListingStore(
-            Context context) throws IOException {
-        return new ProtoStore<>(
-                ChunksMetadataProto.ChunkListing.class,
-                new File(context.getFilesDir().getAbsoluteFile(), CHUNK_LISTING_FOLDER));
-    }
-
-    /** Creates a new instance which stores key value listings in the default location. */
-    public static ProtoStore<KeyValueListingProto.KeyValueListing> createKeyValueListingStore(
-            Context context) throws IOException {
-        return new ProtoStore<>(
-                KeyValueListingProto.KeyValueListing.class,
-                new File(context.getFilesDir().getAbsoluteFile(), KEY_VALUE_LISTING_FOLDER));
-    }
-
-    /**
-     * Creates a new instance which stores protos in the given folder.
-     *
-     * @param storeFolder The location where the serialized form is stored.
-     */
-    @VisibleForTesting
-    ProtoStore(Class<T> clazz, File storeFolder) throws IOException {
-        mClazz = Objects.requireNonNull(clazz);
-        mStoreFolder = ensureDirectoryExistsOrThrow(storeFolder);
-    }
-
-    private static File ensureDirectoryExistsOrThrow(File directory) throws IOException {
-        if (directory.exists() && !directory.isDirectory()) {
-            throw new IOException("Store folder already exists, but isn't a directory.");
-        }
-
-        if (!directory.exists() && !directory.mkdir()) {
-            throw new IOException("Unable to create store folder.");
-        }
-
-        return directory;
-    }
-
-    /**
-     * Returns the chunk listing for the given package, or {@link Optional#empty()} if no listing
-     * exists.
-     */
-    public Optional<T> loadProto(String packageName)
-            throws IOException, IllegalAccessException, InstantiationException,
-            NoSuchMethodException, InvocationTargetException {
-        File file = getFileForPackage(packageName);
-
-        if (!file.exists()) {
-            Slog.d(
-                    TAG,
-                    "No chunk listing existed for " + packageName + ", returning empty listing.");
-            return Optional.empty();
-        }
-
-        AtomicFile protoStore = new AtomicFile(file);
-        byte[] data = protoStore.readFully();
-
-        Constructor<T> constructor = mClazz.getDeclaredConstructor();
-        T proto = constructor.newInstance();
-        MessageNano.mergeFrom(proto, data);
-        return Optional.of(proto);
-    }
-
-    /** Saves a proto to disk, associating it with the given package. */
-    public void saveProto(String packageName, T proto) throws IOException {
-        Objects.requireNonNull(proto);
-        File file = getFileForPackage(packageName);
-
-        try (FileOutputStream os = new FileOutputStream(file)) {
-            os.write(MessageNano.toByteArray(proto));
-        } catch (IOException e) {
-            Slog.e(
-                    TAG,
-                    "Exception occurred when saving the listing for "
-                            + packageName
-                            + ", deleting saved listing.",
-                    e);
-
-            // If a problem occurred when writing the listing then it might be corrupt, so delete
-            // it.
-            file.delete();
-
-            throw e;
-        }
-    }
-
-    /** Deletes the proto for the given package, or does nothing if the package has no proto. */
-    public void deleteProto(String packageName) {
-        File file = getFileForPackage(packageName);
-        file.delete();
-    }
-
-    /** Deletes every proto of this type, for all package names. */
-    public void deleteAllProtos() {
-        File[] files = mStoreFolder.listFiles();
-
-        // We ensure that the storeFolder exists in the constructor, but check just in case it has
-        // mysteriously disappeared.
-        if (files == null) {
-            return;
-        }
-
-        for (File file : files) {
-            file.delete();
-        }
-    }
-
-    private File getFileForPackage(String packageName) {
-        checkPackageName(packageName);
-        return new File(mStoreFolder, packageName);
-    }
-
-    private static void checkPackageName(String packageName) {
-        if (TextUtils.isEmpty(packageName) || packageName.contains("/")) {
-            throw new IllegalArgumentException(
-                    "Package name must not contain '/' or be empty: " + packageName);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/RawBackupWriter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/RawBackupWriter.java
deleted file mode 100644
index b211b0f..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/RawBackupWriter.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/** Writes data straight to an output stream. */
-public class RawBackupWriter implements BackupWriter {
-    private final OutputStream mOutputStream;
-    private long mBytesWritten;
-
-    /** Constructs a new writer which writes bytes to the given output stream. */
-    public RawBackupWriter(OutputStream outputStream) {
-        this.mOutputStream = outputStream;
-    }
-
-    @Override
-    public void writeBytes(byte[] bytes) throws IOException {
-        mOutputStream.write(bytes);
-        mBytesWritten += bytes.length;
-    }
-
-    @Override
-    public void writeChunk(long start, int length) throws IOException {
-        throw new UnsupportedOperationException("RawBackupWriter cannot write existing chunks");
-    }
-
-    @Override
-    public long getBytesWritten() {
-        return mBytesWritten;
-    }
-
-    @Override
-    public void flush() throws IOException {
-        mOutputStream.flush();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java
deleted file mode 100644
index 0e4bd58..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import android.annotation.Nullable;
-
-import com.android.internal.util.Preconditions;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.Charset;
-import java.util.Locale;
-
-/**
- * A {@link DiffScriptWriter} that writes an entire diff script to a single {@link OutputStream}.
- */
-public class SingleStreamDiffScriptWriter implements DiffScriptWriter {
-    static final byte LINE_SEPARATOR = 0xA;
-    private static final Charset UTF_8 = Charset.forName("UTF-8");
-
-    private final int mMaxNewByteChunkSize;
-    private final OutputStream mOutputStream;
-    private final byte[] mByteBuffer;
-    private int mBufferSize = 0;
-    // Each chunk could be written immediately to the output stream. However,
-    // it is possible that chunks may overlap. We therefore cache the most recent
-    // reusable chunk and try to merge it with future chunks.
-    private ByteRange mReusableChunk;
-
-    public SingleStreamDiffScriptWriter(OutputStream outputStream, int maxNewByteChunkSize) {
-        mOutputStream = outputStream;
-        mMaxNewByteChunkSize = maxNewByteChunkSize;
-        mByteBuffer = new byte[maxNewByteChunkSize];
-    }
-
-    @Override
-    public void writeByte(byte b) throws IOException {
-        if (mReusableChunk != null) {
-            writeReusableChunk();
-        }
-        mByteBuffer[mBufferSize++] = b;
-        if (mBufferSize == mMaxNewByteChunkSize) {
-            writeByteBuffer();
-        }
-    }
-
-    @Override
-    public void writeChunk(long chunkStart, int chunkLength) throws IOException {
-        Preconditions.checkArgument(chunkStart >= 0);
-        Preconditions.checkArgument(chunkLength > 0);
-        if (mBufferSize != 0) {
-            writeByteBuffer();
-        }
-
-        if (mReusableChunk != null && mReusableChunk.getEnd() + 1 == chunkStart) {
-            // The new chunk overlaps the old, so combine them into a single byte range.
-            mReusableChunk = mReusableChunk.extend(chunkLength);
-        } else {
-            writeReusableChunk();
-            mReusableChunk = new ByteRange(chunkStart, chunkStart + chunkLength - 1);
-        }
-    }
-
-    @Override
-    public void flush() throws IOException {
-        Preconditions.checkState(!(mBufferSize != 0 && mReusableChunk != null));
-        if (mBufferSize != 0) {
-            writeByteBuffer();
-        }
-        if (mReusableChunk != null) {
-            writeReusableChunk();
-        }
-        mOutputStream.flush();
-    }
-
-    private void writeByteBuffer() throws IOException {
-        mOutputStream.write(Integer.toString(mBufferSize).getBytes(UTF_8));
-        mOutputStream.write(LINE_SEPARATOR);
-        mOutputStream.write(mByteBuffer, 0, mBufferSize);
-        mOutputStream.write(LINE_SEPARATOR);
-        mBufferSize = 0;
-    }
-
-    private void writeReusableChunk() throws IOException {
-        if (mReusableChunk != null) {
-            mOutputStream.write(
-                    String.format(
-                                    Locale.US,
-                                    "%d-%d",
-                                    mReusableChunk.getStart(),
-                                    mReusableChunk.getEnd())
-                            .getBytes(UTF_8));
-            mOutputStream.write(LINE_SEPARATOR);
-            mReusableChunk = null;
-        }
-    }
-
-    /** A factory that creates {@link SingleStreamDiffScriptWriter}s. */
-    public static class Factory implements DiffScriptWriter.Factory {
-        private final int mMaxNewByteChunkSize;
-        private final OutputStreamWrapper mOutputStreamWrapper;
-
-        public Factory(int maxNewByteChunkSize, @Nullable OutputStreamWrapper outputStreamWrapper) {
-            mMaxNewByteChunkSize = maxNewByteChunkSize;
-            mOutputStreamWrapper = outputStreamWrapper;
-        }
-
-        @Override
-        public SingleStreamDiffScriptWriter create(OutputStream outputStream) {
-            if (mOutputStreamWrapper != null) {
-                outputStream = mOutputStreamWrapper.wrap(outputStream);
-            }
-            return new SingleStreamDiffScriptWriter(outputStream, mMaxNewByteChunkSize);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunker.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunker.java
deleted file mode 100644
index 18011f6..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunker.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking.cdc;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import com.android.server.backup.encryption.chunking.Chunker;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.util.Arrays;
-
-/** Splits a stream of bytes into variable-sized chunks, using content-defined chunking. */
-public class ContentDefinedChunker implements Chunker {
-    private static final int WINDOW_SIZE = 31;
-    private static final byte DEFAULT_OUT_BYTE = (byte) 0;
-
-    private final byte[] mChunkBuffer;
-    private final RabinFingerprint64 mRabinFingerprint64;
-    private final FingerprintMixer mFingerprintMixer;
-    private final BreakpointPredicate mBreakpointPredicate;
-    private final int mMinChunkSize;
-    private final int mMaxChunkSize;
-
-    /**
-     * Constructor.
-     *
-     * @param minChunkSize The minimum size of a chunk. No chunk will be produced of a size smaller
-     *     than this except possibly at the very end of the stream.
-     * @param maxChunkSize The maximum size of a chunk. No chunk will be produced of a larger size.
-     * @param rabinFingerprint64 Calculates fingerprints, with which to determine breakpoints.
-     * @param breakpointPredicate Given a Rabin fingerprint, returns whether this ought to be a
-     *     breakpoint.
-     */
-    public ContentDefinedChunker(
-            int minChunkSize,
-            int maxChunkSize,
-            RabinFingerprint64 rabinFingerprint64,
-            FingerprintMixer fingerprintMixer,
-            BreakpointPredicate breakpointPredicate) {
-        checkArgument(
-                minChunkSize >= WINDOW_SIZE,
-                "Minimum chunk size must be greater than window size.");
-        checkArgument(
-                maxChunkSize >= minChunkSize,
-                "Maximum chunk size cannot be smaller than minimum chunk size.");
-        mChunkBuffer = new byte[maxChunkSize];
-        mRabinFingerprint64 = rabinFingerprint64;
-        mBreakpointPredicate = breakpointPredicate;
-        mFingerprintMixer = fingerprintMixer;
-        mMinChunkSize = minChunkSize;
-        mMaxChunkSize = maxChunkSize;
-    }
-
-    /**
-     * Breaks the input stream into variable-sized chunks.
-     *
-     * @param inputStream The input bytes to break into chunks.
-     * @param chunkConsumer A function to process each chunk as it's generated.
-     * @throws IOException Thrown if there is an issue reading from the input stream.
-     * @throws GeneralSecurityException Thrown if the {@link ChunkConsumer} throws it.
-     */
-    @Override
-    public void chunkify(InputStream inputStream, ChunkConsumer chunkConsumer)
-            throws IOException, GeneralSecurityException {
-        int chunkLength;
-        int initialReadLength = mMinChunkSize - WINDOW_SIZE;
-
-        // Performance optimization - there is no reason to calculate fingerprints for windows
-        // ending before the minimum chunk size.
-        while ((chunkLength =
-                        inputStream.read(mChunkBuffer, /*off=*/ 0, /*len=*/ initialReadLength))
-                != -1) {
-            int b;
-            long fingerprint = 0L;
-
-            while ((b = inputStream.read()) != -1) {
-                byte inByte = (byte) b;
-                byte outByte = getCurrentWindowStartByte(chunkLength);
-                mChunkBuffer[chunkLength++] = inByte;
-
-                fingerprint =
-                        mRabinFingerprint64.computeFingerprint64(inByte, outByte, fingerprint);
-
-                if (chunkLength >= mMaxChunkSize
-                        || (chunkLength >= mMinChunkSize
-                                && mBreakpointPredicate.isBreakpoint(
-                                        mFingerprintMixer.mix(fingerprint)))) {
-                    chunkConsumer.accept(Arrays.copyOf(mChunkBuffer, chunkLength));
-                    chunkLength = 0;
-                    break;
-                }
-            }
-
-            if (chunkLength > 0) {
-                chunkConsumer.accept(Arrays.copyOf(mChunkBuffer, chunkLength));
-            }
-        }
-    }
-
-    private byte getCurrentWindowStartByte(int chunkLength) {
-        if (chunkLength < mMinChunkSize) {
-            return DEFAULT_OUT_BYTE;
-        } else {
-            return mChunkBuffer[chunkLength - WINDOW_SIZE];
-        }
-    }
-
-    /** Whether the current fingerprint indicates the end of a chunk. */
-    public interface BreakpointPredicate {
-
-        /**
-         * Returns {@code true} if the fingerprint of the last {@code WINDOW_SIZE} bytes indicates
-         * the chunk ought to end at this position.
-         *
-         * @param fingerprint Fingerprint of the last {@code WINDOW_SIZE} bytes.
-         * @return Whether this ought to be a chunk breakpoint.
-         */
-        boolean isBreakpoint(long fingerprint);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixer.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixer.java
deleted file mode 100644
index e9f3050..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixer.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking.cdc;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.security.InvalidKeyException;
-
-import javax.crypto.SecretKey;
-
-/**
- * Helper for mixing fingerprint with key material.
- *
- * <p>We do this as otherwise the Rabin fingerprint leaks information about the plaintext. i.e., if
- * two users have the same file, it will be partitioned by Rabin in the same way, allowing us to
- * infer that it is the same as another user's file.
- *
- * <p>By mixing the fingerprint with the user's secret key, the chunking method is different on a
- * per key basis. Each application has its own {@link SecretKey}, so we cannot infer that a file is
- * the same even across multiple applications owned by the same user, never mind across multiple
- * users.
- *
- * <p>Instead of directly mixing the fingerprint with the user's secret, we first securely and
- * deterministically derive a secondary chunking key. As Rabin is not a cryptographically secure
- * hash, it might otherwise leak information about the user's secret. This prevents that from
- * happening.
- */
-public class FingerprintMixer {
-    public static final int SALT_LENGTH_BYTES = 256 / Byte.SIZE;
-    private static final String DERIVED_KEY_NAME = "RabinFingerprint64Mixer";
-
-    private final long mAddend;
-    private final long mMultiplicand;
-
-    /**
-     * A new instance from a given secret key and salt. Salt must be the same across incremental
-     * backups, or a different chunking strategy will be used each time, defeating the dedup.
-     *
-     * @param secretKey The application-specific secret.
-     * @param salt The salt.
-     * @throws InvalidKeyException If the encoded form of {@code secretKey} is inaccessible.
-     */
-    public FingerprintMixer(SecretKey secretKey, byte[] salt) throws InvalidKeyException {
-        checkArgument(salt.length == SALT_LENGTH_BYTES, "Requires a 256-bit salt.");
-        byte[] keyBytes = secretKey.getEncoded();
-        if (keyBytes == null) {
-            throw new InvalidKeyException("SecretKey must support encoding for FingerprintMixer.");
-        }
-        byte[] derivedKey =
-                Hkdf.hkdf(keyBytes, salt, DERIVED_KEY_NAME.getBytes(StandardCharsets.UTF_8));
-        ByteBuffer buffer = ByteBuffer.wrap(derivedKey);
-        mAddend = buffer.getLong();
-        // Multiplicand must be odd - otherwise we lose some bits of the Rabin fingerprint when
-        // mixing
-        mMultiplicand = buffer.getLong() | 1;
-    }
-
-    /**
-     * Mixes the fingerprint with the derived key material. This is performed by adding part of the
-     * derived key and multiplying by another part of the derived key (which is forced to be odd, so
-     * that the operation is reversible).
-     *
-     * @param fingerprint A 64-bit Rabin fingerprint.
-     * @return The mixed fingerprint.
-     */
-    long mix(long fingerprint) {
-        return ((fingerprint + mAddend) * mMultiplicand);
-    }
-
-    /** The addend part of the derived key. */
-    long getAddend() {
-        return mAddend;
-    }
-
-    /** The multiplicand part of the derived key. */
-    long getMultiplicand() {
-        return mMultiplicand;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
deleted file mode 100644
index d0776ae..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking.cdc;
-
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Objects;
-
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-
-/**
- * Secure HKDF utils. Allows client to deterministically derive additional key material from a base
- * secret. If the derived key material is compromised, this does not in of itself compromise the
- * root secret.
- *
- * <p>TODO(b/116575321): After all code is ported, rename this class to HkdfUtils.
- */
-public final class Hkdf {
-    private static final byte[] CONSTANT_01 = {0x01};
-    private static final String HmacSHA256 = "HmacSHA256";
-    private static final String AES = "AES";
-
-    /**
-     * Implements HKDF (RFC 5869) with the SHA-256 hash and a 256-bit output key length.
-     *
-     * <p>IMPORTANT: The use or edit of this method requires a security review.
-     *
-     * @param mainKey Main key from which to derive sub-keys.
-     * @param salt A randomly generated 256-bit byte string.
-     * @param data Arbitrary information that is bound to the derived key (i.e., used in its
-     *     creation).
-     * @return Raw derived key bytes = HKDF-SHA256(mainKey, salt, data).
-     * @throws InvalidKeyException If the salt can not be used as a valid key.
-     */
-    static byte[] hkdf(byte[] mainKey, byte[] salt, byte[] data) throws InvalidKeyException {
-        Objects.requireNonNull(mainKey, "HKDF requires main key to be set.");
-        Objects.requireNonNull(salt, "HKDF requires a salt.");
-        Objects.requireNonNull(data, "No data provided to HKDF.");
-        return hkdfSha256Expand(hkdfSha256Extract(mainKey, salt), data);
-    }
-
-    private Hkdf() {}
-
-    /**
-     * The HKDF (RFC 5869) extraction function, using the SHA-256 hash function. This function is
-     * used to pre-process the {@code inputKeyMaterial} and mix it with the {@code salt}, producing
-     * output suitable for use with HKDF expansion function (which produces the actual derived key).
-     *
-     * <p>IMPORTANT: The use or edit of this method requires a security review.
-     *
-     * @see #hkdfSha256Expand(byte[], byte[])
-     * @return HMAC-SHA256(salt, inputKeyMaterial) (salt is the "key" for the HMAC)
-     * @throws InvalidKeyException If the salt can not be used as a valid key.
-     */
-    private static byte[] hkdfSha256Extract(byte[] inputKeyMaterial, byte[] salt)
-            throws InvalidKeyException {
-        // Note that the SecretKey encoding format is defined to be RAW, so the encoded form should
-        // be consistent across implementations.
-        Mac sha256;
-        try {
-            sha256 = Mac.getInstance(HmacSHA256);
-        } catch (NoSuchAlgorithmException e) {
-            // This can not happen - HmacSHA256 is supported by the platform.
-            throw new AssertionError(e);
-        }
-        sha256.init(new SecretKeySpec(salt, AES));
-
-        return sha256.doFinal(inputKeyMaterial);
-    }
-
-    /**
-     * Special case of HKDF (RFC 5869) expansion function, using the SHA-256 hash function and
-     * allowing for a maximum output length of 256 bits.
-     *
-     * <p>IMPORTANT: The use or edit of this method requires a security review.
-     *
-     * @param pseudoRandomKey Generated by {@link #hkdfSha256Extract(byte[], byte[])}.
-     * @param info Arbitrary information the derived key should be bound to.
-     * @return Raw derived key bytes = HMAC-SHA256(pseudoRandomKey, info | 0x01).
-     * @throws InvalidKeyException If the salt can not be used as a valid key.
-     */
-    private static byte[] hkdfSha256Expand(byte[] pseudoRandomKey, byte[] info)
-            throws InvalidKeyException {
-        // Note that RFC 5869 computes number of blocks N = ceil(hash length / output length), but
-        // here we only deal with a 256 bit hash up to a 256 bit output, yielding N=1.
-        Mac sha256;
-        try {
-            sha256 = Mac.getInstance(HmacSHA256);
-        } catch (NoSuchAlgorithmException e) {
-            // This can not happen - HmacSHA256 is supported by the platform.
-            throw new AssertionError(e);
-        }
-        sha256.init(new SecretKeySpec(pseudoRandomKey, AES));
-
-        sha256.update(info);
-        sha256.update(CONSTANT_01);
-        return sha256.doFinal();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpoint.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpoint.java
deleted file mode 100644
index e867e7c..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpoint.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking.cdc;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import com.android.server.backup.encryption.chunking.cdc.ContentDefinedChunker.BreakpointPredicate;
-
-/**
- * Function to determine whether a 64-bit fingerprint ought to be a chunk breakpoint.
- *
- * <p>This works by checking whether there are at least n leading zeros in the fingerprint. n is
- * calculated to on average cause a breakpoint after a given number of trials (provided in the
- * constructor). This allows us to choose a number of trials that gives a desired average chunk
- * size. This works because the fingerprint is pseudo-randomly distributed.
- */
-public class IsChunkBreakpoint implements BreakpointPredicate {
-    private final int mLeadingZeros;
-    private final long mBitmask;
-
-    /**
-     * A new instance that causes a breakpoint after a given number of trials on average.
-     *
-     * @param averageNumberOfTrialsUntilBreakpoint The number of trials after which on average to
-     *     create a new chunk. If this is not a power of 2, some precision is sacrificed (i.e., on
-     *     average, breaks will actually happen after the nearest power of 2 to the average number
-     *     of trials passed in).
-     */
-    public IsChunkBreakpoint(long averageNumberOfTrialsUntilBreakpoint) {
-        checkArgument(
-                averageNumberOfTrialsUntilBreakpoint >= 0,
-                "Average number of trials must be non-negative");
-
-        // Want n leading zeros after t trials.
-        // P(leading zeros = n) = 1/2^n
-        // Expected num trials to get n leading zeros = 1/2^-n
-        // t = 1/2^-n
-        // n = log2(t)
-        mLeadingZeros = (int) Math.round(log2(averageNumberOfTrialsUntilBreakpoint));
-        mBitmask = ~(~0L >>> mLeadingZeros);
-    }
-
-    /**
-     * Returns {@code true} if {@code fingerprint} indicates that there should be a chunk
-     * breakpoint.
-     */
-    @Override
-    public boolean isBreakpoint(long fingerprint) {
-        return (fingerprint & mBitmask) == 0;
-    }
-
-    /** Returns the number of leading zeros in the fingerprint that causes a breakpoint. */
-    public int getLeadingZeros() {
-        return mLeadingZeros;
-    }
-
-    /**
-     * Calculates log base 2 of x. Not the most efficient possible implementation, but it's simple,
-     * obviously correct, and is only invoked on object construction.
-     */
-    private static double log2(double x) {
-        return Math.log(x) / Math.log(2);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64.java
deleted file mode 100644
index 1e14ffa..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking.cdc;
-
-/** Helper to calculate a 64-bit Rabin fingerprint over a 31-byte window. */
-public class RabinFingerprint64 {
-    private static final long DEFAULT_IRREDUCIBLE_POLYNOMIAL_64 = 0x000000000000001BL;
-    private static final int POLYNOMIAL_DEGREE = 64;
-    private static final int SLIDING_WINDOW_SIZE_BYTES = 31;
-
-    private final long mPoly64;
-    // Auxiliary tables to speed up the computation of Rabin fingerprints.
-    private final long[] mTableFP64 = new long[256];
-    private final long[] mTableOutByte = new long[256];
-
-    /**
-     * Constructs a new instance over the given irreducible 64-degree polynomial. It is up to the
-     * caller to determine that the polynomial is irreducible. If it is not the fingerprinting will
-     * not behave as expected.
-     *
-     * @param poly64 The polynomial.
-     */
-    public RabinFingerprint64(long poly64) {
-        mPoly64 = poly64;
-    }
-
-    /** Constructs a new instance using {@code x^64 + x^4 + x + 1} as the irreducible polynomial. */
-    public RabinFingerprint64() {
-        this(DEFAULT_IRREDUCIBLE_POLYNOMIAL_64);
-        computeFingerprintTables64();
-        computeFingerprintTables64Windowed();
-    }
-
-    /**
-     * Computes the fingerprint for the new sliding window given the fingerprint of the previous
-     * sliding window, the byte sliding in, and the byte sliding out.
-     *
-     * @param inChar The new char coming into the sliding window.
-     * @param outChar The left most char sliding out of the window.
-     * @param fingerPrint Fingerprint for previous window.
-     * @return New fingerprint for the new sliding window.
-     */
-    public long computeFingerprint64(byte inChar, byte outChar, long fingerPrint) {
-        return (fingerPrint << 8)
-                ^ (inChar & 0xFF)
-                ^ mTableFP64[(int) (fingerPrint >>> 56)]
-                ^ mTableOutByte[outChar & 0xFF];
-    }
-
-    /** Compute auxiliary tables to speed up the fingerprint computation. */
-    private void computeFingerprintTables64() {
-        long[] degreesRes64 = new long[POLYNOMIAL_DEGREE];
-        degreesRes64[0] = mPoly64;
-        for (int i = 1; i < POLYNOMIAL_DEGREE; i++) {
-            if ((degreesRes64[i - 1] & (1L << 63)) == 0) {
-                degreesRes64[i] = degreesRes64[i - 1] << 1;
-            } else {
-                degreesRes64[i] = (degreesRes64[i - 1] << 1) ^ mPoly64;
-            }
-        }
-        for (int i = 0; i < 256; i++) {
-            int currIndex = i;
-            for (int j = 0; (currIndex > 0) && (j < 8); j++) {
-                if ((currIndex & 0x1) == 1) {
-                    mTableFP64[i] ^= degreesRes64[j];
-                }
-                currIndex >>>= 1;
-            }
-        }
-    }
-
-    /**
-     * Compute auxiliary table {@code mTableOutByte} to facilitate the computing of fingerprints for
-     * sliding windows. This table is to take care of the effect on the fingerprint when the
-     * leftmost byte in the window slides out.
-     */
-    private void computeFingerprintTables64Windowed() {
-        // Auxiliary array degsRes64[8] defined by: <code>degsRes64[i] = x^(8 *
-        // SLIDING_WINDOW_SIZE_BYTES + i) mod this.mPoly64.</code>
-        long[] degsRes64 = new long[8];
-        degsRes64[0] = mPoly64;
-        for (int i = 65; i < 8 * (SLIDING_WINDOW_SIZE_BYTES + 1); i++) {
-            if ((degsRes64[(i - 1) % 8] & (1L << 63)) == 0) {
-                degsRes64[i % 8] = degsRes64[(i - 1) % 8] << 1;
-            } else {
-                degsRes64[i % 8] = (degsRes64[(i - 1) % 8] << 1) ^ mPoly64;
-            }
-        }
-        for (int i = 0; i < 256; i++) {
-            int currIndex = i;
-            for (int j = 0; (currIndex > 0) && (j < 8); j++) {
-                if ((currIndex & 0x1) == 1) {
-                    mTableOutByte[i] ^= degsRes64[j];
-                }
-                currIndex >>>= 1;
-            }
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/client/CryptoBackupServer.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/client/CryptoBackupServer.java
deleted file mode 100644
index d7f7dc7..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/client/CryptoBackupServer.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.encryption.client;
-
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import java.util.Map;
-
-/**
- * Contains methods for communicating with the parts of the backup server relevant to encryption.
- */
-public interface CryptoBackupServer {
-    /**
-     * Uploads an incremental backup to the server.
-     *
-     * <p>Handles setting up and tearing down the connection.
-     *
-     * @param packageName the package to associate the data with
-     * @param oldDocId the id of the previous backup doc in Drive
-     * @param diffScript containing the actual backup data
-     * @param tertiaryKey the wrapped key used to encrypt this backup
-     * @return the id of the new backup doc in Drive.
-     */
-    String uploadIncrementalBackup(
-            String packageName,
-            String oldDocId,
-            byte[] diffScript,
-            WrappedKeyProto.WrappedKey tertiaryKey);
-
-    /**
-     * Uploads non-incremental backup to the server.
-     *
-     * <p>Handles setting up and tearing down the connection.
-     *
-     * @param packageName the package to associate the data with
-     * @param data the actual backup data
-     * @param tertiaryKey the wrapped key used to encrypt this backup
-     * @return the id of the new backup doc in Drive.
-     */
-    String uploadNonIncrementalBackup(
-            String packageName, byte[] data, WrappedKeyProto.WrappedKey tertiaryKey);
-
-    /**
-     * Sets the alias of the active secondary key. This is the alias used to refer to the key in the
-     * {@link java.security.KeyStore}. It is also used to key storage for tertiary keys on the
-     * backup server. Also has to upload all existing tertiary keys, wrapped with the new key.
-     *
-     * @param keyAlias The ID of the secondary key.
-     * @param tertiaryKeys The tertiary keys, wrapped with the new secondary key.
-     */
-    void setActiveSecondaryKeyAlias(
-            String keyAlias, Map<String, WrappedKeyProto.WrappedKey> tertiaryKeys);
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/client/UnexpectedActiveSecondaryOnServerException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/client/UnexpectedActiveSecondaryOnServerException.java
deleted file mode 100644
index 9e31385..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/client/UnexpectedActiveSecondaryOnServerException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.encryption.client;
-
-/**
- * Error thrown when the user attempts to retrieve a key set from the server, but is asking for keys
- * from an inactive secondary.
- *
- * <p>Although we could just return old keys, there is no good reason to do this. It almost
- * certainly indicates a logic error on the client.
- */
-public class UnexpectedActiveSecondaryOnServerException extends Exception {
-    public UnexpectedActiveSecondaryOnServerException(String message) {
-        super(message);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/KeyWrapUtils.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/KeyWrapUtils.java
deleted file mode 100644
index a043c1f..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/KeyWrapUtils.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Locale;
-
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
-
-/** Utility functions for wrapping and unwrapping tertiary keys. */
-public class KeyWrapUtils {
-    private static final String AES_GCM_MODE = "AES/GCM/NoPadding";
-    private static final int GCM_TAG_LENGTH_BYTES = 16;
-    private static final int BITS_PER_BYTE = 8;
-    private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE;
-    private static final String KEY_ALGORITHM = "AES";
-
-    /**
-     * Uses the secondary key to unwrap the wrapped tertiary key.
-     *
-     * @param secondaryKey The secondary key used to wrap the tertiary key.
-     * @param wrappedKey The wrapped tertiary key.
-     * @return The unwrapped tertiary key.
-     * @throws InvalidKeyException if the provided secondary key cannot unwrap the tertiary key.
-     */
-    public static SecretKey unwrap(SecretKey secondaryKey, WrappedKeyProto.WrappedKey wrappedKey)
-            throws InvalidKeyException, NoSuchAlgorithmException,
-                    InvalidAlgorithmParameterException, NoSuchPaddingException {
-        if (wrappedKey.wrapAlgorithm != WrappedKeyProto.WrappedKey.AES_256_GCM) {
-            throw new InvalidKeyException(
-                    String.format(
-                            Locale.US,
-                            "Could not unwrap key wrapped with %s algorithm",
-                            wrappedKey.wrapAlgorithm));
-        }
-
-        if (wrappedKey.metadata == null) {
-            throw new InvalidKeyException("Metadata missing from wrapped tertiary key.");
-        }
-
-        if (wrappedKey.metadata.type != WrappedKeyProto.KeyMetadata.AES_256_GCM) {
-            throw new InvalidKeyException(
-                    String.format(
-                            Locale.US,
-                            "Wrapped key was unexpected %s algorithm. Only support"
-                                + " AES/GCM/NoPadding.",
-                            wrappedKey.metadata.type));
-        }
-
-        Cipher cipher = getCipher();
-
-        cipher.init(
-                Cipher.UNWRAP_MODE,
-                secondaryKey,
-                new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.nonce));
-
-        return (SecretKey) cipher.unwrap(wrappedKey.key, KEY_ALGORITHM, Cipher.SECRET_KEY);
-    }
-
-    /**
-     * Wraps the tertiary key with the secondary key.
-     *
-     * @param secondaryKey The secondary key to use for wrapping.
-     * @param tertiaryKey The key to wrap.
-     * @return The wrapped key.
-     * @throws InvalidKeyException if the key is not good for wrapping.
-     * @throws IllegalBlockSizeException if there is an issue wrapping.
-     */
-    public static WrappedKeyProto.WrappedKey wrap(SecretKey secondaryKey, SecretKey tertiaryKey)
-            throws InvalidKeyException, IllegalBlockSizeException, NoSuchAlgorithmException,
-                    NoSuchPaddingException {
-        Cipher cipher = getCipher();
-        cipher.init(Cipher.WRAP_MODE, secondaryKey);
-
-        WrappedKeyProto.WrappedKey wrappedKey = new WrappedKeyProto.WrappedKey();
-        wrappedKey.key = cipher.wrap(tertiaryKey);
-        wrappedKey.nonce = cipher.getIV();
-        wrappedKey.wrapAlgorithm = WrappedKeyProto.WrappedKey.AES_256_GCM;
-        wrappedKey.metadata = new WrappedKeyProto.KeyMetadata();
-        wrappedKey.metadata.type = WrappedKeyProto.KeyMetadata.AES_256_GCM;
-        return wrappedKey;
-    }
-
-    /**
-     * Rewraps a tertiary key with a new secondary key.
-     *
-     * @param oldSecondaryKey The old secondary key, used to unwrap the tertiary key.
-     * @param newSecondaryKey The new secondary key, used to rewrap the tertiary key.
-     * @param tertiaryKey The tertiary key, wrapped by {@code oldSecondaryKey}.
-     * @return The tertiary key, wrapped by {@code newSecondaryKey}.
-     * @throws InvalidKeyException if the key is not good for wrapping or unwrapping.
-     * @throws IllegalBlockSizeException if there is an issue wrapping.
-     */
-    public static WrappedKeyProto.WrappedKey rewrap(
-            SecretKey oldSecondaryKey,
-            SecretKey newSecondaryKey,
-            WrappedKeyProto.WrappedKey tertiaryKey)
-            throws InvalidKeyException, IllegalBlockSizeException,
-                    InvalidAlgorithmParameterException, NoSuchAlgorithmException,
-                    NoSuchPaddingException {
-        return wrap(newSecondaryKey, unwrap(oldSecondaryKey, tertiaryKey));
-    }
-
-    private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
-        return Cipher.getInstance(AES_GCM_MODE);
-    }
-
-    // Statics only
-    private KeyWrapUtils() {}
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
deleted file mode 100644
index 436c6de8..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.keys;
-
-import android.annotation.IntDef;
-import android.content.Context;
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.RecoveryController;
-import android.util.Slog;
-
-import java.util.Objects;
-
-import javax.crypto.SecretKey;
-
-/**
- * Wraps a {@link RecoveryController}'s {@link SecretKey}. These are kept in "AndroidKeyStore" (a
- * provider for {@link java.security.KeyStore} and {@link javax.crypto.KeyGenerator}. They are also
- * synced with the recoverable key store, wrapped by the primary key. This allows them to be
- * recovered on a user's subsequent device through providing their lock screen secret.
- */
-public class RecoverableKeyStoreSecondaryKey {
-    private static final String TAG = "RecoverableKeyStoreSecondaryKey";
-
-    private final String mAlias;
-    private final SecretKey mSecretKey;
-
-    /**
-     * A new instance.
-     *
-     * @param alias The alias. It is keyed with this in AndroidKeyStore and the recoverable key
-     *     store.
-     * @param secretKey The key.
-     */
-    public RecoverableKeyStoreSecondaryKey(String alias, SecretKey secretKey) {
-        mAlias = Objects.requireNonNull(alias);
-        mSecretKey = Objects.requireNonNull(secretKey);
-    }
-
-    /**
-     * The ID, as stored in the recoverable {@link java.security.KeyStore}, and as used to identify
-     * wrapped tertiary keys on the backup server.
-     */
-    public String getAlias() {
-        return mAlias;
-    }
-
-    /** The secret key, to be used to wrap tertiary keys. */
-    public SecretKey getSecretKey() {
-        return mSecretKey;
-    }
-
-    /**
-     * The status of the key. i.e., whether it's been synced to remote trusted hardware.
-     *
-     * @param context The application context.
-     * @return One of {@link Status#SYNCED}, {@link Status#NOT_SYNCED} or {@link Status#DESTROYED}.
-     */
-    public @Status int getStatus(Context context) {
-        try {
-            return getStatusInternal(context);
-        } catch (InternalRecoveryServiceException e) {
-            Slog.wtf(TAG, "Internal error getting recovery status", e);
-            // Return NOT_SYNCED by default, as we do not want the backups to fail or to repeatedly
-            // attempt to reinitialize.
-            return Status.NOT_SYNCED;
-        }
-    }
-
-    private @Status int getStatusInternal(Context context) throws InternalRecoveryServiceException {
-        int status = RecoveryController.getInstance(context).getRecoveryStatus(mAlias);
-        switch (status) {
-            case RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE:
-                return Status.DESTROYED;
-            case RecoveryController.RECOVERY_STATUS_SYNCED:
-                return Status.SYNCED;
-            case RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS:
-                return Status.NOT_SYNCED;
-            default:
-                // Throw an exception if we encounter a status that doesn't match any of the above.
-                throw new InternalRecoveryServiceException(
-                        "Unexpected status from getRecoveryStatus: " + status);
-        }
-    }
-
-    /** Status of a key in the recoverable key store. */
-    @IntDef({Status.NOT_SYNCED, Status.SYNCED, Status.DESTROYED})
-    public @interface Status {
-        /**
-         * The key has not yet been synced to remote trusted hardware. This may be because the user
-         * has not yet unlocked their device.
-         */
-        int NOT_SYNCED = 1;
-
-        /**
-         * The key has been synced with remote trusted hardware. It should now be recoverable on
-         * another device.
-         */
-        int SYNCED = 2;
-
-        /** The key has been lost forever. This can occur if the user disables their lock screen. */
-        int DESTROYED = 3;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
deleted file mode 100644
index c89076b..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.keys;
-
-import android.content.Context;
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.LockScreenRequiredException;
-import android.security.keystore.recovery.RecoveryController;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import libcore.util.HexEncoding;
-
-import java.security.SecureRandom;
-import java.security.UnrecoverableKeyException;
-import java.util.Optional;
-
-import javax.crypto.SecretKey;
-
-/**
- * Manages generating, deleting, and retrieving secondary keys through {@link RecoveryController}.
- *
- * <p>The recoverable key store will be synced remotely via the {@link RecoveryController}, allowing
- * recovery of keys on other devices owned by the user.
- */
-public class RecoverableKeyStoreSecondaryKeyManager {
-    private static final String BACKUP_KEY_ALIAS_PREFIX =
-            "com.android.server.backup/recoverablekeystore/";
-    private static final int BACKUP_KEY_SUFFIX_LENGTH_BITS = 128;
-    private static final int BITS_PER_BYTE = 8;
-
-    /** A new instance. */
-    public static RecoverableKeyStoreSecondaryKeyManager getInstance(Context context) {
-        return new RecoverableKeyStoreSecondaryKeyManager(
-                RecoveryController.getInstance(context), new SecureRandom());
-    }
-
-    private final RecoveryController mRecoveryController;
-    private final SecureRandom mSecureRandom;
-
-    @VisibleForTesting
-    public RecoverableKeyStoreSecondaryKeyManager(
-            RecoveryController recoveryController, SecureRandom secureRandom) {
-        mRecoveryController = recoveryController;
-        mSecureRandom = secureRandom;
-    }
-
-    /**
-     * Generates a new recoverable key using the {@link RecoveryController}.
-     *
-     * @throws InternalRecoveryServiceException if an unexpected error occurred generating the key.
-     * @throws LockScreenRequiredException if the user does not have a lock screen. A lock screen is
-     *     required to generate a recoverable key.
-     */
-    public RecoverableKeyStoreSecondaryKey generate()
-            throws InternalRecoveryServiceException, LockScreenRequiredException,
-                    UnrecoverableKeyException {
-        String alias = generateId();
-        mRecoveryController.generateKey(alias);
-        SecretKey key = (SecretKey) mRecoveryController.getKey(alias);
-        if (key == null) {
-            throw new InternalRecoveryServiceException(
-                    String.format(
-                            "Generated key %s but could not get it back immediately afterwards.",
-                            alias));
-        }
-        return new RecoverableKeyStoreSecondaryKey(alias, key);
-    }
-
-    /**
-     * Removes the secondary key. This means the key will no longer be recoverable.
-     *
-     * @param alias The alias of the key.
-     * @throws InternalRecoveryServiceException if there was a {@link RecoveryController} error.
-     */
-    public void remove(String alias) throws InternalRecoveryServiceException {
-        mRecoveryController.removeKey(alias);
-    }
-
-    /**
-     * Returns the {@link RecoverableKeyStoreSecondaryKey} with {@code alias} if it is in the {@link
-     * RecoveryController}. Otherwise, {@link Optional#empty()}.
-     */
-    public Optional<RecoverableKeyStoreSecondaryKey> get(String alias)
-            throws InternalRecoveryServiceException, UnrecoverableKeyException {
-        SecretKey secretKey = (SecretKey) mRecoveryController.getKey(alias);
-        return Optional.ofNullable(secretKey)
-                .map(key -> new RecoverableKeyStoreSecondaryKey(alias, key));
-    }
-
-    /**
-     * Generates a new key alias. This has more entropy than a UUID - it can be considered
-     * universally unique.
-     */
-    private String generateId() {
-        byte[] id = new byte[BACKUP_KEY_SUFFIX_LENGTH_BITS / BITS_PER_BYTE];
-        mSecureRandom.nextBytes(id);
-        return BACKUP_KEY_ALIAS_PREFIX + HexEncoding.encodeToString(id);
-    }
-
-    /** Constructs a {@link RecoverableKeyStoreSecondaryKeyManager}. */
-    public interface RecoverableKeyStoreSecondaryKeyManagerProvider {
-        /** Returns a newly constructed {@link RecoverableKeyStoreSecondaryKeyManager}. */
-        RecoverableKeyStoreSecondaryKeyManager get();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RestoreKeyFetcher.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RestoreKeyFetcher.java
deleted file mode 100644
index 6fb958b..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RestoreKeyFetcher.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager.RecoverableKeyStoreSecondaryKeyManagerProvider;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
-import java.util.Optional;
-
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-
-/** Fetches the secondary key and uses it to unwrap the tertiary key during restore. */
-public class RestoreKeyFetcher {
-
-    /**
-     * Retrieves the secondary key with the given alias and uses it to unwrap the given wrapped
-     * tertiary key.
-     *
-     * @param secondaryKeyManagerProvider Provider which creates {@link
-     *     RecoverableKeyStoreSecondaryKeyManager}
-     * @param secondaryKeyAlias Alias of the secondary key used to wrap the tertiary key
-     * @param wrappedTertiaryKey Tertiary key wrapped with the secondary key above
-     * @return The unwrapped tertiary key
-     */
-    public static SecretKey unwrapTertiaryKey(
-            RecoverableKeyStoreSecondaryKeyManagerProvider secondaryKeyManagerProvider,
-            String secondaryKeyAlias,
-            WrappedKeyProto.WrappedKey wrappedTertiaryKey)
-            throws KeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
-                    NoSuchPaddingException {
-        Optional<RecoverableKeyStoreSecondaryKey> secondaryKey =
-                getSecondaryKey(secondaryKeyManagerProvider, secondaryKeyAlias);
-        if (!secondaryKey.isPresent()) {
-            throw new KeyException("No key:" + secondaryKeyAlias);
-        }
-
-        return KeyWrapUtils.unwrap(secondaryKey.get().getSecretKey(), wrappedTertiaryKey);
-    }
-
-    private static Optional<RecoverableKeyStoreSecondaryKey> getSecondaryKey(
-            RecoverableKeyStoreSecondaryKeyManagerProvider secondaryKeyManagerProvider,
-            String secondaryKeyAlias)
-            throws KeyException {
-        try {
-            return secondaryKeyManagerProvider.get().get(secondaryKeyAlias);
-        } catch (InternalRecoveryServiceException | UnrecoverableKeyException e) {
-            throw new KeyException("Could not retrieve key:" + secondaryKeyAlias, e);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java
deleted file mode 100644
index 91b57cf..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import android.content.Context;
-import android.util.Slog;
-
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.tasks.StartSecondaryKeyRotationTask;
-
-import java.io.File;
-import java.time.Clock;
-import java.util.Optional;
-
-/**
- * Helps schedule rotations of secondary keys.
- *
- * <p>TODO(b/72028016) Replace with a job.
- */
-public class SecondaryKeyRotationScheduler {
-
-    private static final String TAG = "SecondaryKeyRotationScheduler";
-    private static final String SENTINEL_FILE_PATH = "force_secondary_key_rotation";
-
-    private final Context mContext;
-    private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
-    private final CryptoSettings mCryptoSettings;
-    private final Clock mClock;
-
-    public SecondaryKeyRotationScheduler(
-            Context context,
-            RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager,
-            CryptoSettings cryptoSettings,
-            Clock clock) {
-        mContext = context;
-        mCryptoSettings = cryptoSettings;
-        mClock = clock;
-        mSecondaryKeyManager = secondaryKeyManager;
-    }
-
-    /**
-     * Returns {@code true} if a sentinel file for forcing secondary key rotation is present. This
-     * is only for testing purposes.
-     */
-    private boolean isForceRotationTestSentinelPresent() {
-        File file = new File(mContext.getFilesDir(), SENTINEL_FILE_PATH);
-        if (file.exists()) {
-            file.delete();
-            return true;
-        }
-        return false;
-    }
-
-    /** Start the key rotation task if it's time to do so */
-    public void startRotationIfScheduled() {
-        if (isForceRotationTestSentinelPresent()) {
-            Slog.i(TAG, "Found force flag for secondary rotation. Starting now.");
-            startRotation();
-            return;
-        }
-
-        Optional<Long> maybeLastRotated = mCryptoSettings.getSecondaryLastRotated();
-        if (!maybeLastRotated.isPresent()) {
-            Slog.v(TAG, "No previous rotation, scheduling from now.");
-            scheduleRotationFromNow();
-            return;
-        }
-
-        long lastRotated = maybeLastRotated.get();
-        long now = mClock.millis();
-
-        if (lastRotated > now) {
-            Slog.i(TAG, "Last rotation was in the future. Clock must have changed. Rotate now.");
-            startRotation();
-            return;
-        }
-
-        long millisSinceLastRotation = now - lastRotated;
-        long rotationInterval = mCryptoSettings.backupSecondaryKeyRotationIntervalMs();
-        if (millisSinceLastRotation >= rotationInterval) {
-            Slog.i(
-                    TAG,
-                    "Last rotation was more than "
-                            + rotationInterval
-                            + "ms ("
-                            + millisSinceLastRotation
-                            + "ms) in the past. Rotate now.");
-            startRotation();
-        }
-
-        Slog.v(TAG, "No rotation required, last " + lastRotated + ".");
-    }
-
-    private void startRotation() {
-        scheduleRotationFromNow();
-        new StartSecondaryKeyRotationTask(mCryptoSettings, mSecondaryKeyManager).run();
-    }
-
-    private void scheduleRotationFromNow() {
-        mCryptoSettings.setSecondaryLastRotated(mClock.millis());
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
deleted file mode 100644
index a425c72..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.keys;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-
-/** 256-bit AES key generator. Each app should have its own separate AES key. */
-public class TertiaryKeyGenerator {
-    private static final int KEY_SIZE_BITS = 256;
-    private static final String KEY_ALGORITHM = "AES";
-
-    private final KeyGenerator mKeyGenerator;
-
-    /** New instance generating keys using {@code secureRandom}. */
-    public TertiaryKeyGenerator(SecureRandom secureRandom) {
-        try {
-            mKeyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
-            mKeyGenerator.init(KEY_SIZE_BITS, secureRandom);
-        } catch (NoSuchAlgorithmException e) {
-            throw new AssertionError(
-                    "Impossible condition: JCE thinks it does not support AES.", e);
-        }
-    }
-
-    /** Generates a new random AES key. */
-    public SecretKey generate() {
-        return mKeyGenerator.generateKey();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyManager.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyManager.java
deleted file mode 100644
index a783579..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyManager.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.util.Slog;
-
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import java.io.IOException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Optional;
-
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-
-/**
- * Gets the correct tertiary key to use during a backup, rotating it if required.
- *
- * <p>Calling any method on this class will count a incremental backup against the app, and the key
- * will be rotated if required.
- */
-public class TertiaryKeyManager {
-
-    private static final String TAG = "TertiaryKeyMgr";
-
-    private final TertiaryKeyStore mKeyStore;
-    private final TertiaryKeyGenerator mKeyGenerator;
-    private final TertiaryKeyRotationScheduler mTertiaryKeyRotationScheduler;
-    private final RecoverableKeyStoreSecondaryKey mSecondaryKey;
-    private final String mPackageName;
-
-    private boolean mKeyRotated;
-    @Nullable private SecretKey mTertiaryKey;
-
-    public TertiaryKeyManager(
-            Context context,
-            SecureRandom secureRandom,
-            TertiaryKeyRotationScheduler tertiaryKeyRotationScheduler,
-            RecoverableKeyStoreSecondaryKey secondaryKey,
-            String packageName) {
-        mSecondaryKey = secondaryKey;
-        mPackageName = packageName;
-        mKeyGenerator = new TertiaryKeyGenerator(secureRandom);
-        mKeyStore = TertiaryKeyStore.newInstance(context, secondaryKey);
-        mTertiaryKeyRotationScheduler = tertiaryKeyRotationScheduler;
-    }
-
-    /**
-     * Returns either the previously used tertiary key, or a new tertiary key if there was no
-     * previous key or it needed to be rotated.
-     */
-    public SecretKey getKey()
-            throws InvalidKeyException, IOException, IllegalBlockSizeException,
-                    NoSuchPaddingException, NoSuchAlgorithmException,
-                    InvalidAlgorithmParameterException {
-        init();
-        return mTertiaryKey;
-    }
-
-    /** Returns the key given by {@link #getKey()} wrapped by the secondary key. */
-    public WrappedKeyProto.WrappedKey getWrappedKey()
-            throws InvalidKeyException, IOException, IllegalBlockSizeException,
-                    NoSuchPaddingException, NoSuchAlgorithmException,
-                    InvalidAlgorithmParameterException {
-        init();
-        return KeyWrapUtils.wrap(mSecondaryKey.getSecretKey(), mTertiaryKey);
-    }
-
-    /**
-     * Returns {@code true} if a new tertiary key was generated at the start of this session,
-     * otherwise {@code false}.
-     */
-    public boolean wasKeyRotated()
-            throws InvalidKeyException, IllegalBlockSizeException, IOException,
-                    NoSuchPaddingException, NoSuchAlgorithmException,
-                    InvalidAlgorithmParameterException {
-        init();
-        return mKeyRotated;
-    }
-
-    private void init()
-            throws IllegalBlockSizeException, InvalidKeyException, IOException,
-                    NoSuchAlgorithmException, NoSuchPaddingException,
-                    InvalidAlgorithmParameterException {
-        if (mTertiaryKey != null) {
-            return;
-        }
-
-        Optional<SecretKey> key = getExistingKeyIfNotRotated();
-
-        if (!key.isPresent()) {
-            Slog.d(TAG, "Generating new tertiary key for " + mPackageName);
-
-            key = Optional.of(mKeyGenerator.generate());
-            mKeyRotated = true;
-            mTertiaryKeyRotationScheduler.recordKeyRotation(mPackageName);
-            mKeyStore.save(mPackageName, key.get());
-        }
-
-        mTertiaryKey = key.get();
-
-        mTertiaryKeyRotationScheduler.recordBackup(mPackageName);
-    }
-
-    private Optional<SecretKey> getExistingKeyIfNotRotated()
-            throws InvalidKeyException, IOException, InvalidAlgorithmParameterException,
-                    NoSuchAlgorithmException, NoSuchPaddingException {
-        if (mTertiaryKeyRotationScheduler.isKeyRotationDue(mPackageName)) {
-            Slog.i(TAG, "Tertiary key rotation was required for " + mPackageName);
-            return Optional.empty();
-        } else {
-            Slog.i(TAG, "Tertiary key rotation was not required");
-            return mKeyStore.load(mPackageName);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationScheduler.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationScheduler.java
deleted file mode 100644
index f16a68d..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationScheduler.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import android.content.Context;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Schedules tertiary key rotations in a staggered fashion.
- *
- * <p>Apps are due a key rotation after a certain number of backups. Rotations are then staggerered
- * over a period of time, through restricting the number of rotations allowed in a 24-hour window.
- * This will causes the apps to enter a staggered cycle of regular rotations.
- *
- * <p>Note: the methods in this class are not optimized to be super fast. They make blocking IO to
- * ensure that scheduler information is committed to disk, so that it is available after the user
- * turns their device off and on. This ought to be fine as
- *
- * <ul>
- *   <li>It will be invoked before a backup, so should never be invoked on the UI thread
- *   <li>It will be invoked before a backup, so the vast amount of time is spent on the backup, not
- *       writing tiny amounts of data to disk.
- * </ul>
- */
-public class TertiaryKeyRotationScheduler {
-    /** Default number of key rotations allowed within 24 hours. */
-    private static final int KEY_ROTATION_LIMIT = 2;
-
-    /** A new instance, using {@code context} to determine where to store state. */
-    public static TertiaryKeyRotationScheduler getInstance(Context context) {
-        TertiaryKeyRotationWindowedCount windowedCount =
-                TertiaryKeyRotationWindowedCount.getInstance(context);
-        TertiaryKeyRotationTracker tracker = TertiaryKeyRotationTracker.getInstance(context);
-        return new TertiaryKeyRotationScheduler(tracker, windowedCount, KEY_ROTATION_LIMIT);
-    }
-
-    private final TertiaryKeyRotationTracker mTracker;
-    private final TertiaryKeyRotationWindowedCount mWindowedCount;
-    private final int mMaximumRotationsPerWindow;
-
-    /**
-     * A new instance.
-     *
-     * @param tracker Tracks how many times each application has backed up.
-     * @param windowedCount Tracks how many rotations have happened in the last 24 hours.
-     * @param maximumRotationsPerWindow The maximum number of key rotations allowed per 24 hours.
-     */
-    @VisibleForTesting
-    TertiaryKeyRotationScheduler(
-            TertiaryKeyRotationTracker tracker,
-            TertiaryKeyRotationWindowedCount windowedCount,
-            int maximumRotationsPerWindow) {
-        mTracker = tracker;
-        mWindowedCount = windowedCount;
-        mMaximumRotationsPerWindow = maximumRotationsPerWindow;
-    }
-
-    /**
-     * Returns {@code true} if the app with {@code packageName} is due having its key rotated.
-     *
-     * <p>This ought to be queried before backing up an app, to determine whether to do an
-     * incremental backup or a full backup. (A full backup forces key rotation.)
-     */
-    public boolean isKeyRotationDue(String packageName) {
-        if (mWindowedCount.getCount() >= mMaximumRotationsPerWindow) {
-            return false;
-        }
-        return mTracker.isKeyRotationDue(packageName);
-    }
-
-    /**
-     * Records that a backup happened for the app with the given {@code packageName}.
-     *
-     * <p>Each backup brings the app closer to the point at which a key rotation is due.
-     */
-    public void recordBackup(String packageName) {
-        mTracker.recordBackup(packageName);
-    }
-
-    /**
-     * Records a key rotation happened for the app with the given {@code packageName}.
-     *
-     * <p>This resets the countdown until the next key rotation is due.
-     */
-    public void recordKeyRotation(String packageName) {
-        mTracker.resetCountdown(packageName);
-        mWindowedCount.record();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
deleted file mode 100644
index 1a281e7..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.Locale;
-
-/**
- * Tracks when a tertiary key rotation is due.
- *
- * <p>After a certain number of incremental backups, the device schedules a full backup, which will
- * generate a new encryption key, effecting a key rotation. We should do this on a regular basis so
- * that if a key does become compromised it has limited value to the attacker.
- *
- * <p>No additional synchronization of this class is provided. Only one instance should be used at
- * any time. This should be fine as there should be no parallelism in backups.
- */
-public class TertiaryKeyRotationTracker {
-    private static final int MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION = 31;
-    private static final String SHARED_PREFERENCES_NAME = "tertiary_key_rotation_tracker";
-
-    private static final String TAG = "TertiaryKeyRotationTracker";
-    private static final boolean DEBUG = false;
-
-    /**
-     * A new instance, using {@code context} to commit data to disk via {@link SharedPreferences}.
-     */
-    public static TertiaryKeyRotationTracker getInstance(Context context) {
-        return new TertiaryKeyRotationTracker(
-                context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE),
-                MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION);
-    }
-
-    private final SharedPreferences mSharedPreferences;
-    private final int mMaxBackupsTillRotation;
-
-    /**
-     * New instance, storing data in {@code sharedPreferences} and initializing backup countdown to
-     * {@code maxBackupsTillRotation}.
-     */
-    @VisibleForTesting
-    TertiaryKeyRotationTracker(SharedPreferences sharedPreferences, int maxBackupsTillRotation) {
-        checkArgument(
-                maxBackupsTillRotation >= 0,
-                String.format(
-                        Locale.US,
-                        "maxBackupsTillRotation should be non-negative but was %d",
-                        maxBackupsTillRotation));
-        mSharedPreferences = sharedPreferences;
-        mMaxBackupsTillRotation = maxBackupsTillRotation;
-    }
-
-    /**
-     * Returns {@code true} if the given app is due having its key rotated.
-     *
-     * @param packageName The package name of the app.
-     */
-    public boolean isKeyRotationDue(String packageName) {
-        return getBackupsSinceRotation(packageName) >= mMaxBackupsTillRotation;
-    }
-
-    /**
-     * Records that an incremental backup has occurred. Each incremental backup brings the app
-     * closer to the time when its key should be rotated.
-     *
-     * @param packageName The package name of the app for which the backup occurred.
-     */
-    public void recordBackup(String packageName) {
-        int backupsSinceRotation = getBackupsSinceRotation(packageName) + 1;
-        mSharedPreferences.edit().putInt(packageName, backupsSinceRotation).apply();
-        if (DEBUG) {
-            Slog.d(
-                    TAG,
-                    String.format(
-                            Locale.US,
-                            "Incremental backup for %s. %d backups until key rotation.",
-                            packageName,
-                            Math.max(
-                                    0,
-                                    mMaxBackupsTillRotation
-                                            - backupsSinceRotation)));
-        }
-    }
-
-    /**
-     * Resets the rotation delay for the given app. Should be invoked after a key rotation.
-     *
-     * @param packageName Package name of the app whose key has rotated.
-     */
-    public void resetCountdown(String packageName) {
-        mSharedPreferences.edit().putInt(packageName, 0).apply();
-    }
-
-    /** Marks all enrolled packages for key rotation. */
-    public void markAllForRotation() {
-        SharedPreferences.Editor editor = mSharedPreferences.edit();
-        for (String packageName : mSharedPreferences.getAll().keySet()) {
-            editor.putInt(packageName, mMaxBackupsTillRotation);
-        }
-        editor.apply();
-    }
-
-    private int getBackupsSinceRotation(String packageName) {
-        return mSharedPreferences.getInt(packageName, 0);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCount.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCount.java
deleted file mode 100644
index b90343a..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCount.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import android.content.Context;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.EOFException;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.time.Clock;
-import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tracks (and commits to disk) how many key rotations have happened in the last 24 hours. This
- * allows us to limit (and therefore stagger) the number of key rotations in a given period of time.
- *
- * <p>Note to engineers thinking of replacing the below with fancier algorithms and data structures:
- * we expect the total size of this count at any time to be below however many rotations we allow in
- * the window, which is going to be in single digits. Any changes that mean we write to disk more
- * frequently, that the code is no longer resistant to clock changes, or that the code is more
- * difficult to understand are almost certainly not worthwhile.
- */
-public class TertiaryKeyRotationWindowedCount {
-    private static final String TAG = "TertiaryKeyRotCount";
-
-    private static final int WINDOW_IN_HOURS = 24;
-    private static final String LOG_FILE_NAME = "tertiary_key_rotation_windowed_count";
-
-    private final Clock mClock;
-    private final File mFile;
-    private ArrayList<Long> mEvents;
-
-    /** Returns a new instance, persisting state to the files dir of {@code context}. */
-    public static TertiaryKeyRotationWindowedCount getInstance(Context context) {
-        File logFile = new File(context.getFilesDir(), LOG_FILE_NAME);
-        return new TertiaryKeyRotationWindowedCount(logFile, Clock.systemDefaultZone());
-    }
-
-    /** A new instance, committing state to {@code file}, and reading time from {@code clock}. */
-    @VisibleForTesting
-    TertiaryKeyRotationWindowedCount(File file, Clock clock) {
-        mFile = file;
-        mClock = clock;
-        mEvents = new ArrayList<>();
-        try {
-            loadFromFile();
-        } catch (IOException e) {
-            Slog.e(TAG, "Error reading " + LOG_FILE_NAME, e);
-        }
-    }
-
-    /** Records a key rotation at the current time. */
-    public void record() {
-        mEvents.add(mClock.millis());
-        compact();
-        try {
-            saveToFile();
-        } catch (IOException e) {
-            Slog.e(TAG, "Error saving " + LOG_FILE_NAME, e);
-        }
-    }
-
-    /** Returns the number of key rotation that have been recorded in the window. */
-    public int getCount() {
-        compact();
-        return mEvents.size();
-    }
-
-    private void compact() {
-        long minimumTimestamp = getMinimumTimestamp();
-        long now = mClock.millis();
-        ArrayList<Long> compacted = new ArrayList<>();
-        for (long event : mEvents) {
-            if (event >= minimumTimestamp && event <= now) {
-                compacted.add(event);
-            }
-        }
-        mEvents = compacted;
-    }
-
-    private long getMinimumTimestamp() {
-        return mClock.millis() - TimeUnit.HOURS.toMillis(WINDOW_IN_HOURS) + 1;
-    }
-
-    private void loadFromFile() throws IOException {
-        if (!mFile.exists()) {
-            return;
-        }
-        try (FileInputStream fis = new FileInputStream(mFile);
-                DataInputStream dis = new DataInputStream(fis)) {
-            while (true) {
-                mEvents.add(dis.readLong());
-            }
-        } catch (EOFException eof) {
-            // expected
-        }
-    }
-
-    private void saveToFile() throws IOException {
-        // File size is maximum number of key rotations in window multiplied by 8 bytes, which is
-        // why
-        // we just overwrite it each time. We expect it will always be less than 100 bytes in size.
-        try (FileOutputStream fos = new FileOutputStream(mFile);
-                DataOutputStream dos = new DataOutputStream(fos)) {
-            for (long event : mEvents) {
-                dos.writeLong(event);
-            }
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java
deleted file mode 100644
index 01444bf..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import android.content.Context;
-import android.util.ArrayMap;
-
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-import com.android.server.backup.encryption.storage.BackupEncryptionDb;
-import com.android.server.backup.encryption.storage.TertiaryKey;
-import com.android.server.backup.encryption.storage.TertiaryKeysTable;
-
-import com.google.protobuf.nano.CodedOutputByteBufferNano;
-
-import java.io.IOException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Map;
-import java.util.Optional;
-
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-
-/**
- * Stores backup package keys. Each application package has its own {@link SecretKey}, which is used
- * to encrypt the backup data. These keys are then wrapped by a master backup key, and stored in
- * their wrapped form on disk and on the backup server.
- *
- * <p>For now this code only implements writing to disk. Once the backup server is ready, it will be
- * extended to sync the keys there, also.
- */
-public class TertiaryKeyStore {
-
-    private final RecoverableKeyStoreSecondaryKey mSecondaryKey;
-    private final BackupEncryptionDb mDatabase;
-
-    /**
-     * Creates an instance, using {@code secondaryKey} to wrap tertiary keys, and storing them in
-     * the database.
-     */
-    public static TertiaryKeyStore newInstance(
-            Context context, RecoverableKeyStoreSecondaryKey secondaryKey) {
-        return new TertiaryKeyStore(secondaryKey, BackupEncryptionDb.newInstance(context));
-    }
-
-    private TertiaryKeyStore(
-            RecoverableKeyStoreSecondaryKey secondaryKey, BackupEncryptionDb database) {
-        mSecondaryKey = secondaryKey;
-        mDatabase = database;
-    }
-
-    /**
-     * Saves the given key.
-     *
-     * @param applicationName The package name of the application for which this key will be used to
-     *     encrypt data. e.g., "com.example.app".
-     * @param key The key.
-     * @throws InvalidKeyException if the backup key is not capable of wrapping.
-     * @throws IOException if there is an issue writing to the database.
-     */
-    public void save(String applicationName, SecretKey key)
-            throws IOException, InvalidKeyException, IllegalBlockSizeException,
-                    NoSuchPaddingException, NoSuchAlgorithmException {
-        checkApplicationName(applicationName);
-
-        byte[] keyBytes = getEncodedKey(KeyWrapUtils.wrap(mSecondaryKey.getSecretKey(), key));
-
-        long pk;
-        try {
-            pk =
-                    mDatabase
-                            .getTertiaryKeysTable()
-                            .addKey(
-                                    new TertiaryKey(
-                                            mSecondaryKey.getAlias(), applicationName, keyBytes));
-        } finally {
-            mDatabase.close();
-        }
-
-        if (pk == -1) {
-            throw new IOException("Failed to commit to db");
-        }
-    }
-
-    /**
-     * Tries to load a key for the given application.
-     *
-     * @param applicationName The package name of the application, e.g. "com.example.app".
-     * @return The key if it is exists, {@link Optional#empty()} ()} otherwise.
-     * @throws InvalidKeyException if the backup key is not good for unwrapping.
-     * @throws IOException if there is a problem loading the key from the database.
-     */
-    public Optional<SecretKey> load(String applicationName)
-            throws IOException, InvalidKeyException, InvalidAlgorithmParameterException,
-                    NoSuchAlgorithmException, NoSuchPaddingException {
-        checkApplicationName(applicationName);
-
-        Optional<TertiaryKey> keyFromDb;
-        try {
-            keyFromDb =
-                    mDatabase
-                            .getTertiaryKeysTable()
-                            .getKey(mSecondaryKey.getAlias(), applicationName);
-        } finally {
-            mDatabase.close();
-        }
-
-        if (!keyFromDb.isPresent()) {
-            return Optional.empty();
-        }
-
-        WrappedKeyProto.WrappedKey wrappedKey =
-                WrappedKeyProto.WrappedKey.parseFrom(keyFromDb.get().getWrappedKeyBytes());
-        return Optional.of(KeyWrapUtils.unwrap(mSecondaryKey.getSecretKey(), wrappedKey));
-    }
-
-    /**
-     * Loads keys for all applications.
-     *
-     * @return All of the keys in a map keyed by package name.
-     * @throws IOException if there is an issue loading from the database.
-     * @throws InvalidKeyException if the backup key is not an appropriate key for unwrapping.
-     */
-    public Map<String, SecretKey> getAll()
-            throws IOException, InvalidKeyException, InvalidAlgorithmParameterException,
-                    NoSuchAlgorithmException, NoSuchPaddingException {
-        Map<String, TertiaryKey> tertiaries;
-        try {
-            tertiaries = mDatabase.getTertiaryKeysTable().getAllKeys(mSecondaryKey.getAlias());
-        } finally {
-            mDatabase.close();
-        }
-
-        Map<String, SecretKey> unwrappedKeys = new ArrayMap<>();
-        for (String applicationName : tertiaries.keySet()) {
-            WrappedKeyProto.WrappedKey wrappedKey =
-                    WrappedKeyProto.WrappedKey.parseFrom(
-                            tertiaries.get(applicationName).getWrappedKeyBytes());
-            unwrappedKeys.put(
-                    applicationName, KeyWrapUtils.unwrap(mSecondaryKey.getSecretKey(), wrappedKey));
-        }
-
-        return unwrappedKeys;
-    }
-
-    /**
-     * Adds all wrapped keys to the database.
-     *
-     * @throws IOException if an error occurred adding a wrapped key.
-     */
-    public void putAll(Map<String, WrappedKeyProto.WrappedKey> wrappedKeysByApplicationName)
-            throws IOException {
-        TertiaryKeysTable tertiaryKeysTable = mDatabase.getTertiaryKeysTable();
-        try {
-
-            for (String applicationName : wrappedKeysByApplicationName.keySet()) {
-                byte[] keyBytes = getEncodedKey(wrappedKeysByApplicationName.get(applicationName));
-                long primaryKey =
-                        tertiaryKeysTable.addKey(
-                                new TertiaryKey(
-                                        mSecondaryKey.getAlias(), applicationName, keyBytes));
-
-                if (primaryKey == -1) {
-                    throw new IOException("Failed to commit to db");
-                }
-            }
-
-        } finally {
-            mDatabase.close();
-        }
-    }
-
-    private static void checkApplicationName(String applicationName) {
-        checkArgument(!applicationName.isEmpty(), "applicationName must not be empty string.");
-        checkArgument(!applicationName.contains("/"), "applicationName must not contain slash.");
-    }
-
-    private byte[] getEncodedKey(WrappedKeyProto.WrappedKey key) throws IOException {
-        byte[] buffer = new byte[key.getSerializedSize()];
-        CodedOutputByteBufferNano out = CodedOutputByteBufferNano.newInstance(buffer);
-        key.writeTo(out);
-        return buffer;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java
deleted file mode 100644
index 56e1c05..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.encryption.kv;
-
-import static com.android.internal.util.Preconditions.checkState;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.ChunkHasher;
-import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
-import com.android.server.backup.encryption.tasks.DecryptedChunkOutput;
-
-import java.io.IOException;
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Builds a key value backup set from plaintext chunks. Computes a digest over the sorted SHA-256
- * hashes of the chunks.
- */
-public class DecryptedChunkKvOutput implements DecryptedChunkOutput {
-    @VisibleForTesting static final String DIGEST_ALGORITHM = "SHA-256";
-
-    private final ChunkHasher mChunkHasher;
-    private final List<KeyValuePairProto.KeyValuePair> mUnsortedPairs = new ArrayList<>();
-    private final List<ChunkHash> mUnsortedHashes = new ArrayList<>();
-    private boolean mClosed;
-
-    /** Constructs a new instance which computers the digest using the given hasher. */
-    public DecryptedChunkKvOutput(ChunkHasher chunkHasher) {
-        mChunkHasher = chunkHasher;
-    }
-
-    @Override
-    public DecryptedChunkOutput open() {
-        // As we don't have any resources there is nothing to open.
-        return this;
-    }
-
-    @Override
-    public void processChunk(byte[] plaintextBuffer, int length)
-            throws IOException, InvalidKeyException {
-        checkState(!mClosed, "Cannot process chunk after close()");
-        KeyValuePairProto.KeyValuePair kvPair = new KeyValuePairProto.KeyValuePair();
-        KeyValuePairProto.KeyValuePair.mergeFrom(kvPair, plaintextBuffer, 0, length);
-        mUnsortedPairs.add(kvPair);
-        // TODO(b/71492289): Update ChunkHasher to accept offset and length so we don't have to copy
-        // the buffer into a smaller array.
-        mUnsortedHashes.add(mChunkHasher.computeHash(Arrays.copyOf(plaintextBuffer, length)));
-    }
-
-    @Override
-    public void close() {
-        // As we don't have any resources there is nothing to close.
-        mClosed = true;
-    }
-
-    @Override
-    public byte[] getDigest() throws NoSuchAlgorithmException {
-        checkState(mClosed, "Must close() before getDigest()");
-        MessageDigest digest = getMessageDigest();
-        Collections.sort(mUnsortedHashes);
-        for (ChunkHash hash : mUnsortedHashes) {
-            digest.update(hash.getHash());
-        }
-        return digest.digest();
-    }
-
-    private static MessageDigest getMessageDigest() throws NoSuchAlgorithmException {
-        return MessageDigest.getInstance(DIGEST_ALGORITHM);
-    }
-
-    /**
-     * Returns the key value pairs from the backup, sorted lexicographically by key.
-     *
-     * <p>You must call {@link #close} first.
-     */
-    public List<KeyValuePairProto.KeyValuePair> getPairs() {
-        checkState(mClosed, "Must close() before getPairs()");
-        Collections.sort(
-                mUnsortedPairs,
-                new Comparator<KeyValuePairProto.KeyValuePair>() {
-                    @Override
-                    public int compare(
-                            KeyValuePairProto.KeyValuePair o1, KeyValuePairProto.KeyValuePair o2) {
-                        return o1.key.compareTo(o2.key);
-                    }
-                });
-        return mUnsortedPairs;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
deleted file mode 100644
index 217304c..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.encryption.kv;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-
-/**
- * Builds a {@link KeyValueListingProto.KeyValueListing}, which is a nano proto and so has no
- * builder.
- */
-public class KeyValueListingBuilder {
-    private final List<KeyValueListingProto.KeyValueEntry> mEntries = new ArrayList<>();
-
-    /** Adds a new pair entry to the listing. */
-    public KeyValueListingBuilder addPair(String key, ChunkHash hash) {
-        checkArgument(key.length() != 0, "Key must have non-zero length");
-        Objects.requireNonNull(hash, "Hash must not be null");
-
-        KeyValueListingProto.KeyValueEntry entry = new KeyValueListingProto.KeyValueEntry();
-        entry.key = key;
-        entry.hash = hash.getHash();
-        mEntries.add(entry);
-
-        return this;
-    }
-
-    /** Adds all pairs contained in a map, where the map is from key to hash. */
-    public KeyValueListingBuilder addAll(Map<String, ChunkHash> map) {
-        for (Entry<String, ChunkHash> entry : map.entrySet()) {
-            addPair(entry.getKey(), entry.getValue());
-        }
-
-        return this;
-    }
-
-    /** Returns a new listing containing all the pairs added so far. */
-    public KeyValueListingProto.KeyValueListing build() {
-        if (mEntries.size() == 0) {
-            return emptyListing();
-        }
-
-        KeyValueListingProto.KeyValueListing listing = new KeyValueListingProto.KeyValueListing();
-        listing.entries = new KeyValueListingProto.KeyValueEntry[mEntries.size()];
-        mEntries.toArray(listing.entries);
-        return listing;
-    }
-
-    /** Returns a new listing which does not contain any pairs. */
-    public static KeyValueListingProto.KeyValueListing emptyListing() {
-        KeyValueListingProto.KeyValueListing listing = new KeyValueListingProto.KeyValueListing();
-        listing.entries = KeyValueListingProto.KeyValueEntry.emptyArray();
-        return listing;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDb.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDb.java
deleted file mode 100644
index 9f6c03a..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDb.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.storage;
-
-import android.content.Context;
-
-/**
- * Backup encryption SQLite database. All instances are threadsafe.
- *
- * <p>The database is automatically opened when accessing one of the tables. After the caller is
- * done they must call {@link #close()}.
- */
-public class BackupEncryptionDb {
-    private final BackupEncryptionDbHelper mHelper;
-
-    /** A new instance, using the storage defined by {@code context}. */
-    public static BackupEncryptionDb newInstance(Context context) {
-        BackupEncryptionDbHelper helper = new BackupEncryptionDbHelper(context);
-        helper.setWriteAheadLoggingEnabled(true);
-        return new BackupEncryptionDb(helper);
-    }
-
-    private BackupEncryptionDb(BackupEncryptionDbHelper helper) {
-        mHelper = helper;
-    }
-
-    public TertiaryKeysTable getTertiaryKeysTable() {
-        return new TertiaryKeysTable(mHelper);
-    }
-
-    /** Deletes the database. */
-    public void clear() throws EncryptionDbException {
-        mHelper.resetDatabase();
-    }
-
-    /**
-     * Closes the database if it is open.
-     *
-     * <p>After calling this, the caller may access one of the tables again which will automatically
-     * reopen the database.
-     */
-    public void close() {
-        mHelper.close();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDbContract.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDbContract.java
deleted file mode 100644
index 5e8a8d9..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDbContract.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.storage;
-
-import android.provider.BaseColumns;
-
-/** Contract for the backup encryption database. Describes tables present. */
-class BackupEncryptionDbContract {
-    /**
-     * Table containing tertiary keys belonging to the user. Tertiary keys are wrapped by a
-     * secondary key, which never leaves {@code AndroidKeyStore} (a provider for {@link
-     * java.security.KeyStore}). Each application has a tertiary key, which is used to encrypt the
-     * backup data.
-     */
-    static class TertiaryKeysEntry implements BaseColumns {
-        static final String TABLE_NAME = "tertiary_keys";
-
-        /** Alias of the secondary key used to wrap the tertiary key. */
-        static final String COLUMN_NAME_SECONDARY_KEY_ALIAS = "secondary_key_alias";
-
-        /** Name of the package to which the tertiary key belongs. */
-        static final String COLUMN_NAME_PACKAGE_NAME = "package_name";
-
-        /** Encrypted bytes of the tertiary key. */
-        static final String COLUMN_NAME_WRAPPED_KEY_BYTES = "wrapped_key_bytes";
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDbHelper.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDbHelper.java
deleted file mode 100644
index c706342..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDbHelper.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.storage;
-
-import static com.android.server.backup.encryption.storage.BackupEncryptionDbContract.TertiaryKeysEntry;
-
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteOpenHelper;
-
-/** Helper for creating an instance of the backup encryption database. */
-class BackupEncryptionDbHelper extends SQLiteOpenHelper {
-    private static final int DATABASE_VERSION = 1;
-    static final String DATABASE_NAME = "backupencryption.db";
-
-    private static final String SQL_CREATE_TERTIARY_KEYS_ENTRY =
-            "CREATE TABLE "
-                    + TertiaryKeysEntry.TABLE_NAME
-                    + " ( "
-                    + TertiaryKeysEntry._ID
-                    + " INTEGER PRIMARY KEY,"
-                    + TertiaryKeysEntry.COLUMN_NAME_SECONDARY_KEY_ALIAS
-                    + " TEXT,"
-                    + TertiaryKeysEntry.COLUMN_NAME_PACKAGE_NAME
-                    + " TEXT,"
-                    + TertiaryKeysEntry.COLUMN_NAME_WRAPPED_KEY_BYTES
-                    + " BLOB,"
-                    + "UNIQUE("
-                    + TertiaryKeysEntry.COLUMN_NAME_SECONDARY_KEY_ALIAS
-                    + ","
-                    + TertiaryKeysEntry.COLUMN_NAME_PACKAGE_NAME
-                    + "))";
-
-    private static final String SQL_DROP_TERTIARY_KEYS_ENTRY =
-            "DROP TABLE IF EXISTS " + TertiaryKeysEntry.TABLE_NAME;
-
-    BackupEncryptionDbHelper(Context context) {
-        super(context, DATABASE_NAME, /*factory=*/ null, DATABASE_VERSION);
-    }
-
-    public void resetDatabase() throws EncryptionDbException {
-        SQLiteDatabase db = getWritableDatabaseSafe();
-        db.execSQL(SQL_DROP_TERTIARY_KEYS_ENTRY);
-        onCreate(db);
-    }
-
-    @Override
-    public void onCreate(SQLiteDatabase db) {
-        db.execSQL(SQL_CREATE_TERTIARY_KEYS_ENTRY);
-    }
-
-    @Override
-    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-        db.execSQL(SQL_DROP_TERTIARY_KEYS_ENTRY);
-        onCreate(db);
-    }
-
-    @Override
-    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-        db.execSQL(SQL_DROP_TERTIARY_KEYS_ENTRY);
-        onCreate(db);
-    }
-
-    /**
-     * Calls {@link #getWritableDatabase()}, but catches the unchecked {@link SQLiteException} and
-     * rethrows {@link EncryptionDbException}.
-     */
-    public SQLiteDatabase getWritableDatabaseSafe() throws EncryptionDbException {
-        try {
-            return super.getWritableDatabase();
-        } catch (SQLiteException e) {
-            throw new EncryptionDbException(e);
-        }
-    }
-
-    /**
-     * Calls {@link #getReadableDatabase()}, but catches the unchecked {@link SQLiteException} and
-     * rethrows {@link EncryptionDbException}.
-     */
-    public SQLiteDatabase getReadableDatabaseSafe() throws EncryptionDbException {
-        try {
-            return super.getReadableDatabase();
-        } catch (SQLiteException e) {
-            throw new EncryptionDbException(e);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/EncryptionDbException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/EncryptionDbException.java
deleted file mode 100644
index 82f7dea..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/EncryptionDbException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.storage;
-
-import java.io.IOException;
-
-/** Thrown when there is a problem reading or writing the encryption database. */
-public class EncryptionDbException extends IOException {
-    public EncryptionDbException(Throwable cause) {
-        super(cause);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/TertiaryKey.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/TertiaryKey.java
deleted file mode 100644
index 39a2c6e..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/TertiaryKey.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.storage;
-
-/** Wrapped bytes of a tertiary key. */
-public class TertiaryKey {
-    private final String mSecondaryKeyAlias;
-    private final String mPackageName;
-    private final byte[] mWrappedKeyBytes;
-
-    /**
-     * Creates a new instance.
-     *
-     * @param secondaryKeyAlias Alias of the secondary used to wrap the key.
-     * @param packageName The package name of the app to which the key belongs.
-     * @param wrappedKeyBytes The wrapped key bytes.
-     */
-    public TertiaryKey(String secondaryKeyAlias, String packageName, byte[] wrappedKeyBytes) {
-        mSecondaryKeyAlias = secondaryKeyAlias;
-        mPackageName = packageName;
-        mWrappedKeyBytes = wrappedKeyBytes;
-    }
-
-    /** Returns the alias of the secondary key used to wrap this tertiary key. */
-    public String getSecondaryKeyAlias() {
-        return mSecondaryKeyAlias;
-    }
-
-    /** Returns the package name of the application this key relates to. */
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    /** Returns the wrapped bytes of the key. */
-    public byte[] getWrappedKeyBytes() {
-        return mWrappedKeyBytes;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/TertiaryKeysTable.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/TertiaryKeysTable.java
deleted file mode 100644
index d8d40c4..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/TertiaryKeysTable.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.storage;
-
-import static com.android.server.backup.encryption.storage.BackupEncryptionDbContract.TertiaryKeysEntry;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.util.ArrayMap;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Optional;
-
-/** Database table for storing and retrieving tertiary keys. */
-public class TertiaryKeysTable {
-    private final BackupEncryptionDbHelper mHelper;
-
-    TertiaryKeysTable(BackupEncryptionDbHelper helper) {
-        mHelper = helper;
-    }
-
-    /**
-     * Adds the {@code tertiaryKey} to the database.
-     *
-     * @return The primary key of the inserted row if successful, -1 otherwise.
-     */
-    public long addKey(TertiaryKey tertiaryKey) throws EncryptionDbException {
-        SQLiteDatabase db = mHelper.getWritableDatabaseSafe();
-        ContentValues values = new ContentValues();
-        values.put(
-                TertiaryKeysEntry.COLUMN_NAME_SECONDARY_KEY_ALIAS,
-                tertiaryKey.getSecondaryKeyAlias());
-        values.put(TertiaryKeysEntry.COLUMN_NAME_PACKAGE_NAME, tertiaryKey.getPackageName());
-        values.put(
-                TertiaryKeysEntry.COLUMN_NAME_WRAPPED_KEY_BYTES, tertiaryKey.getWrappedKeyBytes());
-        return db.replace(TertiaryKeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
-    }
-
-    /** Gets the key wrapped by {@code secondaryKeyAlias} for app with {@code packageName}. */
-    public Optional<TertiaryKey> getKey(String secondaryKeyAlias, String packageName)
-            throws EncryptionDbException {
-        SQLiteDatabase db = mHelper.getReadableDatabaseSafe();
-        String[] projection = {
-            TertiaryKeysEntry._ID,
-            TertiaryKeysEntry.COLUMN_NAME_SECONDARY_KEY_ALIAS,
-            TertiaryKeysEntry.COLUMN_NAME_PACKAGE_NAME,
-            TertiaryKeysEntry.COLUMN_NAME_WRAPPED_KEY_BYTES
-        };
-        String selection =
-                TertiaryKeysEntry.COLUMN_NAME_SECONDARY_KEY_ALIAS
-                        + " = ? AND "
-                        + TertiaryKeysEntry.COLUMN_NAME_PACKAGE_NAME
-                        + " = ?";
-        String[] selectionArguments = {secondaryKeyAlias, packageName};
-
-        try (Cursor cursor =
-                db.query(
-                        TertiaryKeysEntry.TABLE_NAME,
-                        projection,
-                        selection,
-                        selectionArguments,
-                        /*groupBy=*/ null,
-                        /*having=*/ null,
-                        /*orderBy=*/ null)) {
-            int count = cursor.getCount();
-            if (count == 0) {
-                return Optional.empty();
-            }
-
-            cursor.moveToFirst();
-            byte[] wrappedKeyBytes =
-                    cursor.getBlob(
-                            cursor.getColumnIndexOrThrow(
-                                    TertiaryKeysEntry.COLUMN_NAME_WRAPPED_KEY_BYTES));
-            return Optional.of(new TertiaryKey(secondaryKeyAlias, packageName, wrappedKeyBytes));
-        }
-    }
-
-    /** Returns all keys wrapped with {@code tertiaryKeyAlias} as an unmodifiable map. */
-    public Map<String, TertiaryKey> getAllKeys(String secondaryKeyAlias)
-            throws EncryptionDbException {
-        SQLiteDatabase db = mHelper.getReadableDatabaseSafe();
-        String[] projection = {
-            TertiaryKeysEntry._ID,
-            TertiaryKeysEntry.COLUMN_NAME_SECONDARY_KEY_ALIAS,
-            TertiaryKeysEntry.COLUMN_NAME_PACKAGE_NAME,
-            TertiaryKeysEntry.COLUMN_NAME_WRAPPED_KEY_BYTES
-        };
-        String selection = TertiaryKeysEntry.COLUMN_NAME_SECONDARY_KEY_ALIAS + " = ?";
-        String[] selectionArguments = {secondaryKeyAlias};
-
-        Map<String, TertiaryKey> keysByPackageName = new ArrayMap<>();
-        try (Cursor cursor =
-                db.query(
-                        TertiaryKeysEntry.TABLE_NAME,
-                        projection,
-                        selection,
-                        selectionArguments,
-                        /*groupBy=*/ null,
-                        /*having=*/ null,
-                        /*orderBy=*/ null)) {
-            while (cursor.moveToNext()) {
-                String packageName =
-                        cursor.getString(
-                                cursor.getColumnIndexOrThrow(
-                                        TertiaryKeysEntry.COLUMN_NAME_PACKAGE_NAME));
-                byte[] wrappedKeyBytes =
-                        cursor.getBlob(
-                                cursor.getColumnIndexOrThrow(
-                                        TertiaryKeysEntry.COLUMN_NAME_WRAPPED_KEY_BYTES));
-                keysByPackageName.put(
-                        packageName,
-                        new TertiaryKey(secondaryKeyAlias, packageName, wrappedKeyBytes));
-            }
-        }
-        return Collections.unmodifiableMap(keysByPackageName);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ActiveSecondaryNotInKeychainException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ActiveSecondaryNotInKeychainException.java
deleted file mode 100644
index 2e8a61f..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ActiveSecondaryNotInKeychainException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-/**
- * Error thrown when the server's active secondary key does not exist in the user's recoverable
- * keychain. This means the backup data cannot be decrypted, and should be wiped.
- */
-public class ActiveSecondaryNotInKeychainException extends Exception {
-    public ActiveSecondaryNotInKeychainException(String message) {
-        super(message);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupEncrypter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupEncrypter.java
deleted file mode 100644
index 95d0d97..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupEncrypter.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static java.util.Collections.unmodifiableList;
-
-import android.annotation.Nullable;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.EncryptedChunk;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-import javax.crypto.SecretKey;
-
-/** Task which reads data from some source, splits it into chunks and encrypts new chunks. */
-public interface BackupEncrypter {
-    /** The algorithm which we use to compute the digest of the backup file plaintext. */
-    String MESSAGE_DIGEST_ALGORITHM = "SHA-256";
-
-    /**
-     * Splits the backup input into encrypted chunks and encrypts new chunks.
-     *
-     * @param secretKey Key used to encrypt backup.
-     * @param fingerprintMixerSalt Fingerprint mixer salt used for content-defined chunking during a
-     *     full backup. Should be {@code null} for a key-value backup.
-     * @param existingChunks Set of the SHA-256 Macs of chunks the server already has.
-     * @return a result containing an array of new encrypted chunks to upload, and an ordered
-     *     listing of the chunks in the backup file.
-     * @throws IOException if a problem occurs reading from the backup data.
-     * @throws GeneralSecurityException if there is a problem encrypting the data.
-     */
-    Result backup(
-            SecretKey secretKey,
-            @Nullable byte[] fingerprintMixerSalt,
-            Set<ChunkHash> existingChunks)
-            throws IOException, GeneralSecurityException;
-
-    /**
-     * The result of an incremental backup. Contains new encrypted chunks to upload, and an ordered
-     * list of the chunks in the backup file.
-     */
-    class Result {
-        private final List<ChunkHash> mAllChunks;
-        private final List<EncryptedChunk> mNewChunks;
-        private final byte[] mDigest;
-
-        public Result(List<ChunkHash> allChunks, List<EncryptedChunk> newChunks, byte[] digest) {
-            mAllChunks = unmodifiableList(new ArrayList<>(allChunks));
-            mDigest = digest;
-            mNewChunks = unmodifiableList(new ArrayList<>(newChunks));
-        }
-
-        /**
-         * Returns an unmodifiable list of the hashes of all the chunks in the backup, in the order
-         * they appear in the plaintext.
-         */
-        public List<ChunkHash> getAllChunks() {
-            return mAllChunks;
-        }
-
-        /** Returns an unmodifiable list of the new chunks in the backup. */
-        public List<EncryptedChunk> getNewChunks() {
-            return mNewChunks;
-        }
-
-        /** Returns the message digest of the backup. */
-        public byte[] getDigest() {
-            return mDigest;
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTask.java
deleted file mode 100644
index 9bf148d..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTask.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import android.util.Slog;
-import android.util.SparseIntArray;
-
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkOrdering;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunksMetadata;
-
-import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import java.util.Locale;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.GCMParameterSpec;
-
-/**
- * A backup file consists of, in order:
- *
- * <ul>
- *   <li>A randomly ordered sequence of encrypted chunks
- *   <li>A plaintext {@link ChunksMetadata} proto, containing the bytes of an encrypted {@link
- *       ChunkOrdering} proto.
- *   <li>A 64-bit long denoting the offset of the file at which the ChunkOrdering proto starts.
- * </ul>
- *
- * <p>This task decrypts such a blob and writes the plaintext to another file.
- *
- * <p>The backup file has two formats to indicate the boundaries of the chunks in the encrypted
- * file. In {@link ChunksMetadataProto#EXPLICIT_STARTS} mode the chunk ordering contains the start
- * positions of each chunk and the decryptor outputs the chunks in the order they appeared in the
- * plaintext file. In {@link ChunksMetadataProto#INLINE_LENGTHS} mode the length of each encrypted
- * chunk is prepended to the chunk in the file and the decryptor outputs the chunks in no specific
- * order.
- *
- * <p>{@link ChunksMetadataProto#EXPLICIT_STARTS} is for use with full backup (Currently used for
- * all backups as b/77188289 is not implemented yet), {@link ChunksMetadataProto#INLINE_LENGTHS}
- * will be used for kv backup (once b/77188289 is implemented) to avoid re-uploading the chunk
- * ordering (see b/70782620).
- */
-public class BackupFileDecryptorTask {
-    private static final String TAG = "BackupFileDecryptorTask";
-
-    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
-    private static final int GCM_NONCE_LENGTH_BYTES = 12;
-    private static final int GCM_TAG_LENGTH_BYTES = 16;
-    private static final int BITS_PER_BYTE = 8;
-    private static final String READ_MODE = "r";
-    private static final int BYTES_PER_LONG = 64 / BITS_PER_BYTE;
-
-    private final Cipher mCipher;
-    private final SecretKey mSecretKey;
-
-    /**
-     * A new instance.
-     *
-     * @param secretKey The tertiary key used to encrypt the backup blob.
-     */
-    public BackupFileDecryptorTask(SecretKey secretKey)
-            throws NoSuchPaddingException, NoSuchAlgorithmException {
-        this.mCipher = Cipher.getInstance(CIPHER_ALGORITHM);
-        this.mSecretKey = secretKey;
-    }
-
-    /**
-     * Runs the task, reading the encrypted data from {@code input} and writing the plaintext data
-     * to {@code output}.
-     *
-     * @param inputFile The encrypted backup file.
-     * @param decryptedChunkOutput Unopened output to write the plaintext to, which this class will
-     *     open and close during decryption.
-     * @throws IOException if an error occurred reading the encrypted file or writing the plaintext,
-     *     or if one of the protos could not be deserialized.
-     */
-    public void decryptFile(File inputFile, DecryptedChunkOutput decryptedChunkOutput)
-            throws IOException, EncryptedRestoreException, IllegalBlockSizeException,
-                    BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException,
-                    ShortBufferException, NoSuchAlgorithmException {
-        RandomAccessFile input = new RandomAccessFile(inputFile, READ_MODE);
-
-        long metadataOffset = getChunksMetadataOffset(input);
-        ChunksMetadataProto.ChunksMetadata chunksMetadata =
-                getChunksMetadata(input, metadataOffset);
-        ChunkOrdering chunkOrdering = decryptChunkOrdering(chunksMetadata);
-
-        if (chunksMetadata.chunkOrderingType == ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED
-                || chunksMetadata.chunkOrderingType == ChunksMetadataProto.EXPLICIT_STARTS) {
-            Slog.d(TAG, "Using explicit starts");
-            decryptFileWithExplicitStarts(
-                    input, decryptedChunkOutput, chunkOrdering, metadataOffset);
-
-        } else if (chunksMetadata.chunkOrderingType == ChunksMetadataProto.INLINE_LENGTHS) {
-            Slog.d(TAG, "Using inline lengths");
-            decryptFileWithInlineLengths(input, decryptedChunkOutput, metadataOffset);
-
-        } else {
-            throw new UnsupportedEncryptedFileException(
-                    "Unknown chunk ordering type:" + chunksMetadata.chunkOrderingType);
-        }
-
-        if (!Arrays.equals(decryptedChunkOutput.getDigest(), chunkOrdering.checksum)) {
-            throw new MessageDigestMismatchException("Checksums did not match");
-        }
-    }
-
-    private void decryptFileWithExplicitStarts(
-            RandomAccessFile input,
-            DecryptedChunkOutput decryptedChunkOutput,
-            ChunkOrdering chunkOrdering,
-            long metadataOffset)
-            throws IOException, InvalidKeyException, IllegalBlockSizeException,
-                    InvalidAlgorithmParameterException, ShortBufferException, BadPaddingException,
-                    NoSuchAlgorithmException {
-        SparseIntArray chunkLengthsByPosition =
-                getChunkLengths(chunkOrdering.starts, (int) metadataOffset);
-        int largestChunkLength = getLargestChunkLength(chunkLengthsByPosition);
-        byte[] encryptedChunkBuffer = new byte[largestChunkLength];
-        // largestChunkLength is 0 if the backup file contains zero chunks e.g. 0 kv pairs.
-        int plaintextBufferLength =
-                Math.max(0, largestChunkLength - GCM_NONCE_LENGTH_BYTES - GCM_TAG_LENGTH_BYTES);
-        byte[] plaintextChunkBuffer = new byte[plaintextBufferLength];
-
-        try (DecryptedChunkOutput output = decryptedChunkOutput.open()) {
-            for (int start : chunkOrdering.starts) {
-                int length = chunkLengthsByPosition.get(start);
-
-                input.seek(start);
-                input.readFully(encryptedChunkBuffer, 0, length);
-                int plaintextLength =
-                        decryptChunk(encryptedChunkBuffer, length, plaintextChunkBuffer);
-                outputChunk(output, plaintextChunkBuffer, plaintextLength);
-            }
-        }
-    }
-
-    private void decryptFileWithInlineLengths(
-            RandomAccessFile input, DecryptedChunkOutput decryptedChunkOutput, long metadataOffset)
-            throws MalformedEncryptedFileException, IOException, IllegalBlockSizeException,
-                    BadPaddingException, InvalidAlgorithmParameterException, ShortBufferException,
-                    InvalidKeyException, NoSuchAlgorithmException {
-        input.seek(0);
-        try (DecryptedChunkOutput output = decryptedChunkOutput.open()) {
-            while (input.getFilePointer() < metadataOffset) {
-                long start = input.getFilePointer();
-                int encryptedChunkLength = input.readInt();
-
-                if (encryptedChunkLength <= 0) {
-                    // If the length of the encrypted chunk is not positive we will not make
-                    // progress reading the file and so will loop forever.
-                    throw new MalformedEncryptedFileException(
-                            "Encrypted chunk length not positive:" + encryptedChunkLength);
-                }
-
-                if (start + encryptedChunkLength > metadataOffset) {
-                    throw new MalformedEncryptedFileException(
-                            String.format(
-                                    Locale.US,
-                                    "Encrypted chunk longer (%d) than file (%d)",
-                                    encryptedChunkLength,
-                                    metadataOffset));
-                }
-
-                byte[] plaintextChunk = new byte[encryptedChunkLength];
-                byte[] plaintext =
-                        new byte
-                                [encryptedChunkLength
-                                        - GCM_NONCE_LENGTH_BYTES
-                                        - GCM_TAG_LENGTH_BYTES];
-
-                input.readFully(plaintextChunk);
-
-                int plaintextChunkLength =
-                        decryptChunk(plaintextChunk, encryptedChunkLength, plaintext);
-                outputChunk(output, plaintext, plaintextChunkLength);
-            }
-        }
-    }
-
-    private void outputChunk(
-            DecryptedChunkOutput output, byte[] plaintextChunkBuffer, int plaintextLength)
-            throws IOException, InvalidKeyException, NoSuchAlgorithmException {
-        output.processChunk(plaintextChunkBuffer, plaintextLength);
-    }
-
-    /**
-     * Decrypts chunk and returns the length of the plaintext.
-     *
-     * @param encryptedChunkBuffer The encrypted data, prefixed by the nonce.
-     * @param encryptedChunkBufferLength The length of the encrypted chunk (including nonce).
-     * @param plaintextChunkBuffer The buffer into which to write the plaintext chunk.
-     * @return The length of the plaintext chunk.
-     */
-    private int decryptChunk(
-            byte[] encryptedChunkBuffer,
-            int encryptedChunkBufferLength,
-            byte[] plaintextChunkBuffer)
-            throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException,
-                    ShortBufferException, IllegalBlockSizeException {
-
-        mCipher.init(
-                Cipher.DECRYPT_MODE,
-                mSecretKey,
-                new GCMParameterSpec(
-                        GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE,
-                        encryptedChunkBuffer,
-                        0,
-                        GCM_NONCE_LENGTH_BYTES));
-
-        return mCipher.doFinal(
-                encryptedChunkBuffer,
-                GCM_NONCE_LENGTH_BYTES,
-                encryptedChunkBufferLength - GCM_NONCE_LENGTH_BYTES,
-                plaintextChunkBuffer);
-    }
-
-    /** Given all the lengths, returns the largest length. */
-    private int getLargestChunkLength(SparseIntArray lengths) {
-        int maxSeen = 0;
-        for (int i = 0; i < lengths.size(); i++) {
-            maxSeen = Math.max(maxSeen, lengths.valueAt(i));
-        }
-        return maxSeen;
-    }
-
-    /**
-     * From a list of the starting position of each chunk in the correct order of the backup data,
-     * calculates a mapping from start position to length of that chunk.
-     *
-     * @param starts The start positions of chunks, in order.
-     * @param chunkOrderingPosition Where the {@link ChunkOrdering} proto starts, used to calculate
-     *     the length of the last chunk.
-     * @return The mapping.
-     */
-    private SparseIntArray getChunkLengths(int[] starts, int chunkOrderingPosition) {
-        int[] boundaries = Arrays.copyOf(starts, starts.length + 1);
-        boundaries[boundaries.length - 1] = chunkOrderingPosition;
-        Arrays.sort(boundaries);
-
-        SparseIntArray lengths = new SparseIntArray();
-        for (int i = 0; i < boundaries.length - 1; i++) {
-            lengths.put(boundaries[i], boundaries[i + 1] - boundaries[i]);
-        }
-        return lengths;
-    }
-
-    /**
-     * Reads and decrypts the {@link ChunkOrdering} from the {@link ChunksMetadata}.
-     *
-     * @param metadata The metadata.
-     * @return The ordering.
-     * @throws InvalidProtocolBufferNanoException if there is an issue deserializing the proto.
-     */
-    private ChunkOrdering decryptChunkOrdering(ChunksMetadata metadata)
-            throws InvalidProtocolBufferNanoException, InvalidAlgorithmParameterException,
-                    InvalidKeyException, BadPaddingException, IllegalBlockSizeException,
-                    UnsupportedEncryptedFileException {
-        assertCryptoSupported(metadata);
-
-        mCipher.init(
-                Cipher.DECRYPT_MODE,
-                mSecretKey,
-                new GCMParameterSpec(
-                        GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE,
-                        metadata.chunkOrdering,
-                        0,
-                        GCM_NONCE_LENGTH_BYTES));
-
-        byte[] decrypted =
-                mCipher.doFinal(
-                        metadata.chunkOrdering,
-                        GCM_NONCE_LENGTH_BYTES,
-                        metadata.chunkOrdering.length - GCM_NONCE_LENGTH_BYTES);
-
-        return ChunkOrdering.parseFrom(decrypted);
-    }
-
-    /**
-     * Asserts that the Cipher and MessageDigest algorithms in the backup metadata are supported.
-     * For now we only support SHA-256 for checksum and 256-bit AES/GCM/NoPadding for the Cipher.
-     *
-     * @param chunksMetadata The file metadata.
-     * @throws UnsupportedEncryptedFileException if any algorithm is unsupported.
-     */
-    private void assertCryptoSupported(ChunksMetadata chunksMetadata)
-            throws UnsupportedEncryptedFileException {
-        if (chunksMetadata.checksumType != ChunksMetadataProto.SHA_256) {
-            // For now we only support SHA-256.
-            throw new UnsupportedEncryptedFileException(
-                    "Unrecognized checksum type for backup (this version of backup only supports"
-                        + " SHA-256): "
-                            + chunksMetadata.checksumType);
-        }
-
-        if (chunksMetadata.cipherType != ChunksMetadataProto.AES_256_GCM) {
-            throw new UnsupportedEncryptedFileException(
-                    "Unrecognized cipher type for backup (this version of backup only supports"
-                        + " AES-256-GCM: "
-                            + chunksMetadata.cipherType);
-        }
-    }
-
-    /**
-     * Reads the offset of the {@link ChunksMetadata} proto from the end of the file.
-     *
-     * @return The offset.
-     * @throws IOException if there is an error reading.
-     */
-    private long getChunksMetadataOffset(RandomAccessFile input) throws IOException {
-        input.seek(input.length() - BYTES_PER_LONG);
-        return input.readLong();
-    }
-
-    /**
-     * Reads the {@link ChunksMetadata} proto from the given position in the file.
-     *
-     * @param input The encrypted file.
-     * @param position The position where the proto starts.
-     * @return The proto.
-     * @throws IOException if there is an issue reading the file or deserializing the proto.
-     */
-    private ChunksMetadata getChunksMetadata(RandomAccessFile input, long position)
-            throws IOException, MalformedEncryptedFileException {
-        long length = input.length();
-        if (position >= length || position < 0) {
-            throw new MalformedEncryptedFileException(
-                    String.format(
-                            Locale.US,
-                            "%d is not valid position for chunks metadata in file of %d bytes",
-                            position,
-                            length));
-        }
-
-        // Read chunk ordering bytes
-        input.seek(position);
-        long chunksMetadataLength = input.length() - BYTES_PER_LONG - position;
-        byte[] chunksMetadataBytes = new byte[(int) chunksMetadataLength];
-        input.readFully(chunksMetadataBytes);
-
-        try {
-            return ChunksMetadata.parseFrom(chunksMetadataBytes);
-        } catch (InvalidProtocolBufferNanoException e) {
-            throw new MalformedEncryptedFileException(
-                    String.format(
-                            Locale.US,
-                            "Could not read chunks metadata at position %d of file of %d bytes",
-                            position,
-                            length));
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypter.java
deleted file mode 100644
index 45798d3..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypter.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import android.util.Slog;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.ChunkEncryptor;
-import com.android.server.backup.encryption.chunking.ChunkHasher;
-import com.android.server.backup.encryption.chunking.EncryptedChunk;
-import com.android.server.backup.encryption.chunking.cdc.ContentDefinedChunker;
-import com.android.server.backup.encryption.chunking.cdc.FingerprintMixer;
-import com.android.server.backup.encryption.chunking.cdc.IsChunkBreakpoint;
-import com.android.server.backup.encryption.chunking.cdc.RabinFingerprint64;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.crypto.SecretKey;
-
-/**
- * Splits backup data into variable-sized chunks using content-defined chunking, then encrypts the
- * chunks. Given a hash of the SHA-256s of existing chunks, performs an incremental backup (i.e.,
- * only encrypts new chunks).
- */
-public class BackupStreamEncrypter implements BackupEncrypter {
-    private static final String TAG = "BackupStreamEncryptor";
-
-    private final InputStream mData;
-    private final int mMinChunkSizeBytes;
-    private final int mMaxChunkSizeBytes;
-    private final int mAverageChunkSizeBytes;
-
-    /**
-     * A new instance over the given distribution of chunk sizes.
-     *
-     * @param data The data to be backed up.
-     * @param minChunkSizeBytes The minimum chunk size. No chunk will be smaller than this.
-     * @param maxChunkSizeBytes The maximum chunk size. No chunk will be larger than this.
-     * @param averageChunkSizeBytes The average chunk size. The mean size of chunks will be roughly
-     *     this (with a few tens of bytes of overhead for the initialization vector and message
-     *     authentication code).
-     */
-    public BackupStreamEncrypter(
-            InputStream data,
-            int minChunkSizeBytes,
-            int maxChunkSizeBytes,
-            int averageChunkSizeBytes) {
-        this.mData = data;
-        this.mMinChunkSizeBytes = minChunkSizeBytes;
-        this.mMaxChunkSizeBytes = maxChunkSizeBytes;
-        this.mAverageChunkSizeBytes = averageChunkSizeBytes;
-    }
-
-    @Override
-    public Result backup(
-            SecretKey secretKey, byte[] fingerprintMixerSalt, Set<ChunkHash> existingChunks)
-            throws IOException, GeneralSecurityException {
-        MessageDigest messageDigest =
-                MessageDigest.getInstance(BackupEncrypter.MESSAGE_DIGEST_ALGORITHM);
-        RabinFingerprint64 rabinFingerprint64 = new RabinFingerprint64();
-        FingerprintMixer fingerprintMixer = new FingerprintMixer(secretKey, fingerprintMixerSalt);
-        IsChunkBreakpoint isChunkBreakpoint =
-                new IsChunkBreakpoint(mAverageChunkSizeBytes - mMinChunkSizeBytes);
-        ContentDefinedChunker chunker =
-                new ContentDefinedChunker(
-                        mMinChunkSizeBytes,
-                        mMaxChunkSizeBytes,
-                        rabinFingerprint64,
-                        fingerprintMixer,
-                        isChunkBreakpoint);
-        ChunkHasher chunkHasher = new ChunkHasher(secretKey);
-        ChunkEncryptor encryptor = new ChunkEncryptor(secretKey, new SecureRandom());
-        Set<ChunkHash> includedChunks = new HashSet<>();
-        // New chunks will be added only once to this list, even if they occur multiple times.
-        List<EncryptedChunk> newChunks = new ArrayList<>();
-        // All chunks (including multiple occurrences) will be added to the chunkListing.
-        List<ChunkHash> chunkListing = new ArrayList<>();
-
-        includedChunks.addAll(existingChunks);
-
-        chunker.chunkify(
-                mData,
-                chunk -> {
-                    messageDigest.update(chunk);
-                    ChunkHash key = chunkHasher.computeHash(chunk);
-
-                    if (!includedChunks.contains(key)) {
-                        newChunks.add(encryptor.encrypt(key, chunk));
-                        includedChunks.add(key);
-                    }
-                    chunkListing.add(key);
-                });
-
-        Slog.i(
-                TAG,
-                String.format(
-                        "Chunks: %d total, %d unique, %d new",
-                        chunkListing.size(), new HashSet<>(chunkListing).size(), newChunks.size()));
-        return new Result(
-                Collections.unmodifiableList(chunkListing),
-                Collections.unmodifiableList(newChunks),
-                messageDigest.digest());
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTask.java
deleted file mode 100644
index 8f35db6..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTask.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import android.content.Context;
-import android.util.Slog;
-
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.chunking.ProtoStore;
-import com.android.server.backup.encryption.storage.BackupEncryptionDb;
-import com.android.server.backup.encryption.storage.EncryptionDbException;
-
-import java.io.IOException;
-
-/**
- * Task to clear local crypto state.
- *
- * <p>Needs to run whenever the user changes their backup account.
- */
-public class ClearCryptoStateTask {
-    private static final String TAG = "ClearCryptoStateTask";
-
-    private final Context mContext;
-    private final CryptoSettings mCryptoSettings;
-
-    /**
-     * A new instance.
-     *
-     * @param context for finding local storage.
-     * @param cryptoSettings to clear
-     */
-    public ClearCryptoStateTask(Context context, CryptoSettings cryptoSettings) {
-        mContext = context;
-        mCryptoSettings = cryptoSettings;
-    }
-
-    /** Deletes all local state for backup (not restore). */
-    public void run() {
-        Slog.d(TAG, "Clearing local crypto state.");
-        try {
-            BackupEncryptionDb.newInstance(mContext).clear();
-        } catch (EncryptionDbException e) {
-            Slog.e(TAG, "Error clearing encryption database", e);
-        }
-        mCryptoSettings.clearAllSettingsForBackup();
-        try {
-            ProtoStore.createChunkListingStore(mContext).deleteAllProtos();
-        } catch (IOException e) {
-            Slog.e(TAG, "Error clearing chunk listing store", e);
-        }
-        try {
-            ProtoStore.createKeyValueListingStore(mContext).deleteAllProtos();
-        } catch (IOException e) {
-            Slog.e(TAG, "Error clearing key-value store", e);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
deleted file mode 100644
index f67f100..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * Accepts the plaintext bytes of decrypted chunks and writes them to some output. Also keeps track
- * of the message digest of the chunks.
- */
-public interface DecryptedChunkOutput extends Closeable {
-    /**
-     * Opens whatever output the implementation chooses, ready to process chunks.
-     *
-     * @return {@code this}, to allow use with try-with-resources
-     */
-    DecryptedChunkOutput open() throws IOException, NoSuchAlgorithmException;
-
-    /**
-     * Writes the plaintext bytes of chunk to whatever output the implementation chooses. Also
-     * updates the digest with the chunk.
-     *
-     * <p>You must call {@link #open()} before this method, and you may not call it after calling
-     * {@link Closeable#close()}.
-     *
-     * @param plaintextBuffer An array containing the bytes of the plaintext of the chunk, starting
-     *     at index 0.
-     * @param length The length in bytes of the plaintext contained in {@code plaintextBuffer}.
-     */
-    void processChunk(byte[] plaintextBuffer, int length)
-            throws IOException, InvalidKeyException, NoSuchAlgorithmException;
-
-    /**
-     * Returns the message digest of all the chunks processed by {@link #processChunk}.
-     *
-     * <p>You must call {@link Closeable#close()} before calling this method.
-     */
-    byte[] getDigest() throws NoSuchAlgorithmException;
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedBackupTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedBackupTask.java
deleted file mode 100644
index ef13f23..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedBackupTask.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import android.annotation.Nullable;
-import android.annotation.TargetApi;
-import android.os.Build.VERSION_CODES;
-import android.util.Slog;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.BackupFileBuilder;
-import com.android.server.backup.encryption.chunking.EncryptedChunk;
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.GCMParameterSpec;
-
-/**
- * Task which reads encrypted chunks from a {@link BackupEncrypter}, builds a backup file and
- * uploads it to the server.
- */
-@TargetApi(VERSION_CODES.P)
-public class EncryptedBackupTask {
-    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
-    private static final int GCM_NONCE_LENGTH_BYTES = 12;
-    private static final int GCM_TAG_LENGTH_BYTES = 16;
-    private static final int BITS_PER_BYTE = 8;
-
-    private static final String TAG = "EncryptedBackupTask";
-
-    private final CryptoBackupServer mCryptoBackupServer;
-    private final SecureRandom mSecureRandom;
-    private final String mPackageName;
-    private final ByteArrayOutputStream mBackupDataOutput;
-    private final BackupEncrypter mBackupEncrypter;
-    private final AtomicBoolean mCancelled;
-
-    /** Creates a new instance which reads data from the given input stream. */
-    public EncryptedBackupTask(
-            CryptoBackupServer cryptoBackupServer,
-            SecureRandom secureRandom,
-            String packageName,
-            BackupEncrypter backupEncrypter) {
-        mCryptoBackupServer = cryptoBackupServer;
-        mSecureRandom = secureRandom;
-        mPackageName = packageName;
-        mBackupEncrypter = backupEncrypter;
-
-        mBackupDataOutput = new ByteArrayOutputStream();
-        mCancelled = new AtomicBoolean(false);
-    }
-
-    /**
-     * Creates a non-incremental backup file and uploads it to the server.
-     *
-     * @param fingerprintMixerSalt Fingerprint mixer salt used for content-defined chunking during a
-     *     full backup. May be {@code null} for a key-value backup.
-     */
-    public ChunksMetadataProto.ChunkListing performNonIncrementalBackup(
-            SecretKey tertiaryKey,
-            WrappedKeyProto.WrappedKey wrappedTertiaryKey,
-            @Nullable byte[] fingerprintMixerSalt)
-            throws IOException, GeneralSecurityException {
-
-        ChunksMetadataProto.ChunkListing newChunkListing =
-                performBackup(
-                        tertiaryKey,
-                        fingerprintMixerSalt,
-                        BackupFileBuilder.createForNonIncremental(mBackupDataOutput),
-                        new HashSet<>());
-
-        throwIfCancelled();
-
-        newChunkListing.documentId =
-                mCryptoBackupServer.uploadNonIncrementalBackup(
-                        mPackageName, mBackupDataOutput.toByteArray(), wrappedTertiaryKey);
-
-        return newChunkListing;
-    }
-
-    /** Creates an incremental backup file and uploads it to the server. */
-    public ChunksMetadataProto.ChunkListing performIncrementalBackup(
-            SecretKey tertiaryKey,
-            WrappedKeyProto.WrappedKey wrappedTertiaryKey,
-            ChunksMetadataProto.ChunkListing oldChunkListing)
-            throws IOException, GeneralSecurityException {
-
-        ChunksMetadataProto.ChunkListing newChunkListing =
-                performBackup(
-                        tertiaryKey,
-                        oldChunkListing.fingerprintMixerSalt,
-                        BackupFileBuilder.createForIncremental(mBackupDataOutput, oldChunkListing),
-                        getChunkHashes(oldChunkListing));
-
-        throwIfCancelled();
-
-        String oldDocumentId = oldChunkListing.documentId;
-        Slog.v(TAG, "Old doc id: " + oldDocumentId);
-
-        newChunkListing.documentId =
-                mCryptoBackupServer.uploadIncrementalBackup(
-                        mPackageName,
-                        oldDocumentId,
-                        mBackupDataOutput.toByteArray(),
-                        wrappedTertiaryKey);
-        return newChunkListing;
-    }
-
-    /**
-     * Signals to the task that the backup has been cancelled. If the upload has not yet started
-     * then the task will not upload any data to the server or save the new chunk listing.
-     */
-    public void cancel() {
-        mCancelled.getAndSet(true);
-    }
-
-    private void throwIfCancelled() {
-        if (mCancelled.get()) {
-            throw new CancellationException("EncryptedBackupTask was cancelled");
-        }
-    }
-
-    private ChunksMetadataProto.ChunkListing performBackup(
-            SecretKey tertiaryKey,
-            @Nullable byte[] fingerprintMixerSalt,
-            BackupFileBuilder backupFileBuilder,
-            Set<ChunkHash> existingChunkHashes)
-            throws IOException, GeneralSecurityException {
-        BackupEncrypter.Result result =
-                mBackupEncrypter.backup(tertiaryKey, fingerprintMixerSalt, existingChunkHashes);
-        backupFileBuilder.writeChunks(result.getAllChunks(), buildChunkMap(result.getNewChunks()));
-
-        ChunksMetadataProto.ChunkOrdering chunkOrdering =
-                backupFileBuilder.getNewChunkOrdering(result.getDigest());
-        backupFileBuilder.finish(buildMetadata(tertiaryKey, chunkOrdering));
-
-        return backupFileBuilder.getNewChunkListing(fingerprintMixerSalt);
-    }
-
-    /** Returns a set containing the hashes of every chunk in the given listing. */
-    private static Set<ChunkHash> getChunkHashes(ChunksMetadataProto.ChunkListing chunkListing) {
-        Set<ChunkHash> hashes = new HashSet<>();
-        for (ChunksMetadataProto.Chunk chunk : chunkListing.chunks) {
-            hashes.add(new ChunkHash(chunk.hash));
-        }
-        return hashes;
-    }
-
-    /** Returns a map from chunk hash to chunk containing every chunk in the given list. */
-    private static Map<ChunkHash, EncryptedChunk> buildChunkMap(List<EncryptedChunk> chunks) {
-        Map<ChunkHash, EncryptedChunk> chunkMap = new HashMap<>();
-        for (EncryptedChunk chunk : chunks) {
-            chunkMap.put(chunk.key(), chunk);
-        }
-        return chunkMap;
-    }
-
-    private ChunksMetadataProto.ChunksMetadata buildMetadata(
-            SecretKey tertiaryKey, ChunksMetadataProto.ChunkOrdering chunkOrdering)
-            throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
-                    InvalidAlgorithmParameterException, NoSuchAlgorithmException,
-                    ShortBufferException, NoSuchPaddingException {
-        ChunksMetadataProto.ChunksMetadata metaData = new ChunksMetadataProto.ChunksMetadata();
-        metaData.cipherType = ChunksMetadataProto.AES_256_GCM;
-        metaData.checksumType = ChunksMetadataProto.SHA_256;
-        metaData.chunkOrdering = encryptChunkOrdering(tertiaryKey, chunkOrdering);
-        return metaData;
-    }
-
-    private byte[] encryptChunkOrdering(
-            SecretKey tertiaryKey, ChunksMetadataProto.ChunkOrdering chunkOrdering)
-            throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
-                    NoSuchPaddingException, NoSuchAlgorithmException,
-                    InvalidAlgorithmParameterException, ShortBufferException {
-        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
-
-        byte[] nonce = generateNonce();
-
-        cipher.init(
-                Cipher.ENCRYPT_MODE,
-                tertiaryKey,
-                new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE, nonce));
-
-        byte[] orderingBytes = ChunksMetadataProto.ChunkOrdering.toByteArray(chunkOrdering);
-        // We prepend the nonce to the ordering.
-        byte[] output =
-                Arrays.copyOf(
-                        nonce,
-                        GCM_NONCE_LENGTH_BYTES + orderingBytes.length + GCM_TAG_LENGTH_BYTES);
-
-        cipher.doFinal(
-                orderingBytes,
-                /*inputOffset=*/ 0,
-                /*inputLen=*/ orderingBytes.length,
-                output,
-                /*outputOffset=*/ GCM_NONCE_LENGTH_BYTES);
-
-        return output;
-    }
-
-    private byte[] generateNonce() {
-        byte[] nonce = new byte[GCM_NONCE_LENGTH_BYTES];
-        mSecureRandom.nextBytes(nonce);
-        return nonce;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
deleted file mode 100644
index 71588f6..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.android.internal.util.Preconditions.checkState;
-
-import android.annotation.Nullable;
-import android.app.backup.BackupTransport;
-import android.content.Context;
-import android.util.Slog;
-
-import com.android.server.backup.encryption.FullBackupDataProcessor;
-import com.android.server.backup.encryption.StreamUtils;
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.security.SecureRandom;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-/**
- * Accepts backup data from a {@link InputStream} and passes it to the encrypted full data backup
- * path.
- */
-public class EncryptedFullBackupDataProcessor implements FullBackupDataProcessor {
-
-    private static final String TAG = "EncryptedFullBackupDP";
-
-    private final Context mContext;
-    private final ExecutorService mExecutorService;
-    private final CryptoBackupServer mCryptoBackupServer;
-    private final SecureRandom mSecureRandom;
-    private final RecoverableKeyStoreSecondaryKey mSecondaryKey;
-    private final String mPackageName;
-
-    @Nullable private InputStream mInputStream;
-    @Nullable private PipedOutputStream mOutputStream;
-    @Nullable private EncryptedFullBackupTask mBackupTask;
-    @Nullable private Future<Void> mBackupTaskFuture;
-    @Nullable private FullBackupCallbacks mFullBackupCallbacks;
-
-    public EncryptedFullBackupDataProcessor(
-            Context context,
-            ExecutorService executorService,
-            CryptoBackupServer cryptoBackupServer,
-            SecureRandom secureRandom,
-            RecoverableKeyStoreSecondaryKey secondaryKey,
-            String packageName) {
-        mContext = Objects.requireNonNull(context);
-        mExecutorService = Objects.requireNonNull(executorService);
-        mCryptoBackupServer = Objects.requireNonNull(cryptoBackupServer);
-        mSecureRandom = Objects.requireNonNull(secureRandom);
-        mSecondaryKey = Objects.requireNonNull(secondaryKey);
-        mPackageName = Objects.requireNonNull(packageName);
-    }
-
-    @Override
-    public boolean initiate(InputStream inputStream) throws IOException {
-        checkState(mBackupTask == null, "initiate() twice");
-
-        this.mInputStream = inputStream;
-        mOutputStream = new PipedOutputStream();
-
-        mBackupTask =
-                EncryptedFullBackupTask.newInstance(
-                        mContext,
-                        mCryptoBackupServer,
-                        mSecureRandom,
-                        mSecondaryKey,
-                        mPackageName,
-                        new PipedInputStream(mOutputStream));
-
-        return true;
-    }
-
-    @Override
-    public void start() {
-        checkState(mBackupTask != null, "start() before initiate()");
-        mBackupTaskFuture = mExecutorService.submit(mBackupTask);
-    }
-
-    @Override
-    public int pushData(int numBytes) {
-        checkState(
-                mBackupTaskFuture != null && mInputStream != null && mOutputStream != null,
-                "pushData() before start()");
-
-        // If the upload has failed then stop without pushing any more bytes.
-        if (mBackupTaskFuture.isDone()) {
-            Optional<Exception> exception = getTaskException();
-            Slog.e(TAG, "Encrypted upload failed", exception.orElse(null));
-            if (exception.isPresent()) {
-                reportNetworkFailureIfNecessary(exception.get());
-
-                if (exception.get().getCause() instanceof SizeQuotaExceededException) {
-                    return BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
-                }
-            }
-
-            return BackupTransport.TRANSPORT_ERROR;
-        }
-
-        try {
-            StreamUtils.copyStream(mInputStream, mOutputStream, numBytes);
-        } catch (IOException e) {
-            Slog.e(TAG, "IOException when processing backup", e);
-            return BackupTransport.TRANSPORT_ERROR;
-        }
-
-        return BackupTransport.TRANSPORT_OK;
-    }
-
-    @Override
-    public void cancel() {
-        checkState(mBackupTaskFuture != null && mBackupTask != null, "cancel() before start()");
-        mBackupTask.cancel();
-        closeStreams();
-    }
-
-    @Override
-    public int finish() {
-        checkState(mBackupTaskFuture != null, "finish() before start()");
-
-        // getTaskException() waits for the task to finish. We must close the streams first, which
-        // causes the task to finish, otherwise it will block forever.
-        closeStreams();
-        Optional<Exception> exception = getTaskException();
-
-        if (exception.isPresent()) {
-            Slog.e(TAG, "Exception during encrypted full backup", exception.get());
-            reportNetworkFailureIfNecessary(exception.get());
-
-            if (exception.get().getCause() instanceof SizeQuotaExceededException) {
-                return BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
-            }
-            return BackupTransport.TRANSPORT_ERROR;
-
-        } else {
-            if (mFullBackupCallbacks != null) {
-                mFullBackupCallbacks.onSuccess();
-            }
-
-            return BackupTransport.TRANSPORT_OK;
-        }
-    }
-
-    private void closeStreams() {
-        StreamUtils.closeQuietly(mInputStream);
-        StreamUtils.closeQuietly(mOutputStream);
-    }
-
-    @Override
-    public void handleCheckSizeRejectionZeroBytes() {
-        cancel();
-    }
-
-    @Override
-    public void handleCheckSizeRejectionQuotaExceeded() {
-        cancel();
-    }
-
-    @Override
-    public void handleSendBytesQuotaExceeded() {
-        cancel();
-    }
-
-    @Override
-    public void attachCallbacks(FullBackupCallbacks fullBackupCallbacks) {
-        this.mFullBackupCallbacks = fullBackupCallbacks;
-    }
-
-    private void reportNetworkFailureIfNecessary(Exception exception) {
-        if (!(exception.getCause() instanceof SizeQuotaExceededException)
-                && mFullBackupCallbacks != null) {
-            mFullBackupCallbacks.onTransferFailed();
-        }
-    }
-
-    private Optional<Exception> getTaskException() {
-        if (mBackupTaskFuture != null) {
-            try {
-                mBackupTaskFuture.get();
-            } catch (InterruptedException | ExecutionException e) {
-                return Optional.of(e);
-            }
-        }
-        return Optional.empty();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTask.java
deleted file mode 100644
index a938d71..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTask.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import android.content.Context;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.backup.encryption.StreamUtils;
-import com.android.server.backup.encryption.chunking.ProtoStore;
-import com.android.server.backup.encryption.chunking.cdc.FingerprintMixer;
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.TertiaryKeyManager;
-import com.android.server.backup.encryption.keys.TertiaryKeyRotationScheduler;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.Optional;
-import java.util.concurrent.Callable;
-
-import javax.crypto.SecretKey;
-
-/**
- * Task which reads a stream of plaintext full backup data, chunks it, encrypts it and uploads it to
- * the server.
- *
- * <p>Once the backup completes or fails, closes the input stream.
- */
-public class EncryptedFullBackupTask implements Callable<Void> {
-    private static final String TAG = "EncryptedFullBackupTask";
-
-    private static final int MIN_CHUNK_SIZE_BYTES = 2 * 1024;
-    private static final int MAX_CHUNK_SIZE_BYTES = 64 * 1024;
-    private static final int AVERAGE_CHUNK_SIZE_BYTES = 4 * 1024;
-
-    // TODO(b/69350270): Remove this hard-coded salt and related logic once we feel confident that
-    // incremental backup has happened at least once for all existing packages/users since we moved
-    // to
-    // using a randomly generated salt.
-    //
-    // The hard-coded fingerprint mixer salt was used for a short time period before replaced by one
-    // that is randomly generated on initial non-incremental backup and stored in ChunkListing to be
-    // reused for succeeding incremental backups. If an old ChunkListing does not have a
-    // fingerprint_mixer_salt, we assume that it was last backed up before a randomly generated salt
-    // is used so we use the hardcoded salt and set ChunkListing#fingerprint_mixer_salt to this
-    // value.
-    // Eventually all backup ChunkListings will have this field set and then we can remove the
-    // default
-    // value in the code.
-    static final byte[] DEFAULT_FINGERPRINT_MIXER_SALT =
-            Arrays.copyOf(new byte[] {20, 23}, FingerprintMixer.SALT_LENGTH_BYTES);
-
-    private final ProtoStore<ChunkListing> mChunkListingStore;
-    private final TertiaryKeyManager mTertiaryKeyManager;
-    private final InputStream mInputStream;
-    private final EncryptedBackupTask mTask;
-    private final String mPackageName;
-    private final SecureRandom mSecureRandom;
-
-    /** Creates a new instance with the default min, max and average chunk sizes. */
-    public static EncryptedFullBackupTask newInstance(
-            Context context,
-            CryptoBackupServer cryptoBackupServer,
-            SecureRandom secureRandom,
-            RecoverableKeyStoreSecondaryKey secondaryKey,
-            String packageName,
-            InputStream inputStream)
-            throws IOException {
-        EncryptedBackupTask encryptedBackupTask =
-                new EncryptedBackupTask(
-                        cryptoBackupServer,
-                        secureRandom,
-                        packageName,
-                        new BackupStreamEncrypter(
-                                inputStream,
-                                MIN_CHUNK_SIZE_BYTES,
-                                MAX_CHUNK_SIZE_BYTES,
-                                AVERAGE_CHUNK_SIZE_BYTES));
-        TertiaryKeyManager tertiaryKeyManager =
-                new TertiaryKeyManager(
-                        context,
-                        secureRandom,
-                        TertiaryKeyRotationScheduler.getInstance(context),
-                        secondaryKey,
-                        packageName);
-
-        return new EncryptedFullBackupTask(
-                ProtoStore.createChunkListingStore(context),
-                tertiaryKeyManager,
-                encryptedBackupTask,
-                inputStream,
-                packageName,
-                new SecureRandom());
-    }
-
-    @VisibleForTesting
-    EncryptedFullBackupTask(
-            ProtoStore<ChunkListing> chunkListingStore,
-            TertiaryKeyManager tertiaryKeyManager,
-            EncryptedBackupTask task,
-            InputStream inputStream,
-            String packageName,
-            SecureRandom secureRandom) {
-        mChunkListingStore = chunkListingStore;
-        mTertiaryKeyManager = tertiaryKeyManager;
-        mInputStream = inputStream;
-        mTask = task;
-        mPackageName = packageName;
-        mSecureRandom = secureRandom;
-    }
-
-    @Override
-    public Void call() throws Exception {
-        try {
-            Optional<ChunkListing> maybeOldChunkListing =
-                    mChunkListingStore.loadProto(mPackageName);
-
-            if (maybeOldChunkListing.isPresent()) {
-                Slog.i(TAG, "Found previous chunk listing for " + mPackageName);
-            }
-
-            // If the key has been rotated then we must re-encrypt all of the backup data.
-            if (mTertiaryKeyManager.wasKeyRotated()) {
-                Slog.i(
-                        TAG,
-                        "Key was rotated or newly generated for "
-                                + mPackageName
-                                + ", so performing a full backup.");
-                maybeOldChunkListing = Optional.empty();
-                mChunkListingStore.deleteProto(mPackageName);
-            }
-
-            SecretKey tertiaryKey = mTertiaryKeyManager.getKey();
-            WrappedKeyProto.WrappedKey wrappedTertiaryKey = mTertiaryKeyManager.getWrappedKey();
-
-            ChunkListing newChunkListing;
-            if (!maybeOldChunkListing.isPresent()) {
-                byte[] fingerprintMixerSalt = new byte[FingerprintMixer.SALT_LENGTH_BYTES];
-                mSecureRandom.nextBytes(fingerprintMixerSalt);
-                newChunkListing =
-                        mTask.performNonIncrementalBackup(
-                                tertiaryKey, wrappedTertiaryKey, fingerprintMixerSalt);
-            } else {
-                ChunkListing oldChunkListing = maybeOldChunkListing.get();
-
-                if (oldChunkListing.fingerprintMixerSalt == null
-                        || oldChunkListing.fingerprintMixerSalt.length == 0) {
-                    oldChunkListing.fingerprintMixerSalt = DEFAULT_FINGERPRINT_MIXER_SALT;
-                }
-
-                newChunkListing =
-                        mTask.performIncrementalBackup(
-                                tertiaryKey, wrappedTertiaryKey, oldChunkListing);
-            }
-
-            mChunkListingStore.saveProto(mPackageName, newChunkListing);
-            Slog.v(TAG, "Saved chunk listing for " + mPackageName);
-        } catch (IOException e) {
-            Slog.e(TAG, "Storage exception, wiping state");
-            mChunkListingStore.deleteProto(mPackageName);
-            throw e;
-        } finally {
-            StreamUtils.closeQuietly(mInputStream);
-        }
-
-        return null;
-    }
-
-    /**
-     * Signals to the task that the backup has been cancelled. If the upload has not yet started
-     * then the task will not upload any data to the server or save the new chunk listing.
-     *
-     * <p>You must then terminate the input stream.
-     */
-    public void cancel() {
-        mTask.cancel();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTask.java
deleted file mode 100644
index 04381af..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTask.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import android.annotation.Nullable;
-import android.content.Context;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.backup.encryption.FullRestoreDataProcessor;
-import com.android.server.backup.encryption.FullRestoreDownloader;
-import com.android.server.backup.encryption.StreamUtils;
-import com.android.server.backup.encryption.chunking.DecryptedChunkFileOutput;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.ShortBufferException;
-
-/** Downloads the encrypted backup file, decrypts it and passes the data to backup manager. */
-public class EncryptedFullRestoreTask implements FullRestoreDataProcessor {
-    private static final String DEFAULT_TEMPORARY_FOLDER = "encrypted_restore_temp";
-    private static final String ENCRYPTED_FILE_NAME = "encrypted_restore";
-    private static final String DECRYPTED_FILE_NAME = "decrypted_restore";
-
-    private final FullRestoreToFileTask mFullRestoreToFileTask;
-    private final BackupFileDecryptorTask mBackupFileDecryptorTask;
-    private final File mEncryptedFile;
-    private final File mDecryptedFile;
-    @Nullable private InputStream mDecryptedFileInputStream;
-
-    /**
-     * Creates a new task which stores temporary files in the files directory.
-     *
-     * @param fullRestoreDownloader which will download the backup file
-     * @param tertiaryKey which the backup file is encrypted with
-     */
-    public static EncryptedFullRestoreTask newInstance(
-            Context context, FullRestoreDownloader fullRestoreDownloader, SecretKey tertiaryKey)
-            throws NoSuchAlgorithmException, NoSuchPaddingException {
-        File temporaryFolder = new File(context.getFilesDir(), DEFAULT_TEMPORARY_FOLDER);
-        temporaryFolder.mkdirs();
-        return new EncryptedFullRestoreTask(
-                temporaryFolder, fullRestoreDownloader, new BackupFileDecryptorTask(tertiaryKey));
-    }
-
-    @VisibleForTesting
-    EncryptedFullRestoreTask(
-            File temporaryFolder,
-            FullRestoreDownloader fullRestoreDownloader,
-            BackupFileDecryptorTask backupFileDecryptorTask) {
-        checkArgument(temporaryFolder.isDirectory(), "Temporary folder must be existing directory");
-
-        mEncryptedFile = new File(temporaryFolder, ENCRYPTED_FILE_NAME);
-        mDecryptedFile = new File(temporaryFolder, DECRYPTED_FILE_NAME);
-
-        mFullRestoreToFileTask = new FullRestoreToFileTask(fullRestoreDownloader);
-        mBackupFileDecryptorTask = backupFileDecryptorTask;
-    }
-
-    /**
-     * Reads the next decrypted bytes into the given buffer.
-     *
-     * <p>During the first call this method will download the backup file from the server, decrypt
-     * it and save it to disk. It will then read the bytes from the file on disk.
-     *
-     * <p>Once this method has read all the bytes of the file, the caller must call {@link #finish}
-     * to clean up.
-     *
-     * @return the number of bytes read, or {@code -1} on reaching the end of the file
-     */
-    @Override
-    public int readNextChunk(byte[] buffer) throws IOException {
-        if (mDecryptedFileInputStream == null) {
-            try {
-                mDecryptedFileInputStream = downloadAndDecryptBackup();
-            } catch (BadPaddingException
-                    | InvalidKeyException
-                    | NoSuchAlgorithmException
-                    | IllegalBlockSizeException
-                    | ShortBufferException
-                    | EncryptedRestoreException
-                    | InvalidAlgorithmParameterException e) {
-                throw new IOException("Encryption issue", e);
-            }
-        }
-
-        return mDecryptedFileInputStream.read(buffer);
-    }
-
-    private InputStream downloadAndDecryptBackup()
-            throws IOException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
-                    IllegalBlockSizeException, ShortBufferException, EncryptedRestoreException,
-                    InvalidAlgorithmParameterException {
-        mFullRestoreToFileTask.restoreToFile(mEncryptedFile);
-        mBackupFileDecryptorTask.decryptFile(
-                mEncryptedFile, new DecryptedChunkFileOutput(mDecryptedFile));
-        mEncryptedFile.delete();
-        return new BufferedInputStream(new FileInputStream(mDecryptedFile));
-    }
-
-    /** Cleans up temporary files. */
-    @Override
-    public void finish(FullRestoreDownloader.FinishType unusedFinishType) {
-        // The download is finished and log sent during RestoreToFileTask#restoreToFile(), so we
-        // don't need to do either of those things here.
-
-        StreamUtils.closeQuietly(mDecryptedFileInputStream);
-        mEncryptedFile.delete();
-        mDecryptedFile.delete();
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTask.java
deleted file mode 100644
index 619438c..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTask.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import android.annotation.Nullable;
-import android.app.backup.BackupDataInput;
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.LockScreenRequiredException;
-import android.util.Pair;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.chunking.ProtoStore;
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
-import com.android.server.backup.encryption.keys.TertiaryKeyManager;
-import com.android.server.backup.encryption.keys.TertiaryKeyRotationScheduler;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
-
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.SecureRandom;
-import java.security.UnrecoverableKeyException;
-import java.util.Optional;
-
-// TODO(b/141975695): Create a base class for EncryptedKvBackupTask and EncryptedFullBackupTask.
-/** Performs encrypted key value backup, handling rotating the tertiary key as necessary. */
-public class EncryptedKvBackupTask {
-    private static final String TAG = "EncryptedKvBackupTask";
-
-    private final TertiaryKeyManager mTertiaryKeyManager;
-    private final RecoverableKeyStoreSecondaryKey mSecondaryKey;
-    private final ProtoStore<KeyValueListingProto.KeyValueListing> mKeyValueListingStore;
-    private final ProtoStore<ChunksMetadataProto.ChunkListing> mChunkListingStore;
-    private final KvBackupEncrypter mKvBackupEncrypter;
-    private final EncryptedBackupTask mEncryptedBackupTask;
-    private final String mPackageName;
-
-    /** Constructs new instances of {@link EncryptedKvBackupTask}. */
-    public static class EncryptedKvBackupTaskFactory {
-        /**
-         * Creates a new instance.
-         *
-         * <p>Either initializes encrypted backup or loads an existing secondary key as necessary.
-         *
-         * @param cryptoSettings to load secondary key state from
-         * @param fileDescriptor to read the backup data from
-         */
-        public EncryptedKvBackupTask newInstance(
-                Context context,
-                SecureRandom secureRandom,
-                CryptoBackupServer cryptoBackupServer,
-                CryptoSettings cryptoSettings,
-                RecoverableKeyStoreSecondaryKeyManager
-                                .RecoverableKeyStoreSecondaryKeyManagerProvider
-                        recoverableSecondaryKeyManagerProvider,
-                ParcelFileDescriptor fileDescriptor,
-                String packageName)
-                throws IOException, UnrecoverableKeyException, LockScreenRequiredException,
-                        InternalRecoveryServiceException, InvalidKeyException {
-            RecoverableKeyStoreSecondaryKey secondaryKey =
-                    new InitializeRecoverableSecondaryKeyTask(
-                                    context,
-                                    cryptoSettings,
-                                    recoverableSecondaryKeyManagerProvider.get(),
-                                    cryptoBackupServer)
-                            .run();
-            KvBackupEncrypter backupEncrypter =
-                    new KvBackupEncrypter(new BackupDataInput(fileDescriptor.getFileDescriptor()));
-            TertiaryKeyManager tertiaryKeyManager =
-                    new TertiaryKeyManager(
-                            context,
-                            secureRandom,
-                            TertiaryKeyRotationScheduler.getInstance(context),
-                            secondaryKey,
-                            packageName);
-
-            return new EncryptedKvBackupTask(
-                    tertiaryKeyManager,
-                    ProtoStore.createKeyValueListingStore(context),
-                    secondaryKey,
-                    ProtoStore.createChunkListingStore(context),
-                    backupEncrypter,
-                    new EncryptedBackupTask(
-                            cryptoBackupServer, secureRandom, packageName, backupEncrypter),
-                    packageName);
-        }
-    }
-
-    @VisibleForTesting
-    EncryptedKvBackupTask(
-            TertiaryKeyManager tertiaryKeyManager,
-            ProtoStore<KeyValueListingProto.KeyValueListing> keyValueListingStore,
-            RecoverableKeyStoreSecondaryKey secondaryKey,
-            ProtoStore<ChunksMetadataProto.ChunkListing> chunkListingStore,
-            KvBackupEncrypter kvBackupEncrypter,
-            EncryptedBackupTask encryptedBackupTask,
-            String packageName) {
-        mTertiaryKeyManager = tertiaryKeyManager;
-        mSecondaryKey = secondaryKey;
-        mKeyValueListingStore = keyValueListingStore;
-        mChunkListingStore = chunkListingStore;
-        mKvBackupEncrypter = kvBackupEncrypter;
-        mEncryptedBackupTask = encryptedBackupTask;
-        mPackageName = packageName;
-    }
-
-    /**
-     * Reads backup data from the file descriptor provided in the construtor, encrypts it and
-     * uploads it to the server.
-     *
-     * <p>The {@code incremental} flag indicates if the backup data provided is incremental or a
-     * complete set. Incremental backup is not possible if no previous crypto state exists, or the
-     * tertiary key must be rotated in the next backup. If the caller requests incremental backup
-     * but it is not possible, then the backup will not start and this method will throw {@link
-     * NonIncrementalBackupRequiredException}.
-     *
-     * <p>TODO(b/70704456): Update return code to indicate that we require non-incremental backup.
-     *
-     * @param incremental {@code true} if the data provided is a diff from the previous backup,
-     *     {@code false} if it is a complete set
-     * @throws NonIncrementalBackupRequiredException if the caller provides an incremental backup but the task
-     *     requires non-incremental backup
-     */
-    public void performBackup(boolean incremental)
-            throws GeneralSecurityException, IOException, NoSuchMethodException,
-            InstantiationException, IllegalAccessException, InvocationTargetException,
-            NonIncrementalBackupRequiredException {
-        if (mTertiaryKeyManager.wasKeyRotated()) {
-            Slog.d(TAG, "Tertiary key is new so clearing package state.");
-            deleteListings(mPackageName);
-        }
-
-        Optional<Pair<KeyValueListingProto.KeyValueListing, ChunksMetadataProto.ChunkListing>>
-                oldListings = getListingsAndEnsureConsistency(mPackageName);
-
-        if (oldListings.isPresent() && !incremental) {
-            Slog.d(
-                    TAG,
-                    "Non-incremental backup requested but incremental state existed, clearing it");
-            deleteListings(mPackageName);
-            oldListings = Optional.empty();
-        }
-
-        if (!oldListings.isPresent() && incremental) {
-            // If we don't have any state then we require a non-incremental backup, but this backup
-            // is incremental.
-            throw new NonIncrementalBackupRequiredException();
-        }
-
-        if (oldListings.isPresent()) {
-            mKvBackupEncrypter.setOldKeyValueListing(oldListings.get().first);
-        }
-
-        ChunksMetadataProto.ChunkListing newChunkListing;
-        if (oldListings.isPresent()) {
-            Slog.v(TAG, "Old listings existed, performing incremental backup");
-            newChunkListing =
-                    mEncryptedBackupTask.performIncrementalBackup(
-                            mTertiaryKeyManager.getKey(),
-                            mTertiaryKeyManager.getWrappedKey(),
-                            oldListings.get().second);
-        } else {
-            Slog.v(TAG, "Old listings did not exist, performing non-incremental backup");
-            // kv backups don't use this salt because they don't involve content-defined chunking.
-            byte[] fingerprintMixerSalt = null;
-            newChunkListing =
-                    mEncryptedBackupTask.performNonIncrementalBackup(
-                            mTertiaryKeyManager.getKey(),
-                            mTertiaryKeyManager.getWrappedKey(),
-                            fingerprintMixerSalt);
-        }
-
-        Slog.v(TAG, "Backup and upload succeeded, saving new listings");
-        saveListings(mPackageName, mKvBackupEncrypter.getNewKeyValueListing(), newChunkListing);
-    }
-
-    private Optional<Pair<KeyValueListingProto.KeyValueListing, ChunksMetadataProto.ChunkListing>>
-            getListingsAndEnsureConsistency(String packageName)
-                    throws IOException, InvocationTargetException, NoSuchMethodException,
-                            InstantiationException, IllegalAccessException {
-        Optional<KeyValueListingProto.KeyValueListing> keyValueListing =
-                mKeyValueListingStore.loadProto(packageName);
-        Optional<ChunksMetadataProto.ChunkListing> chunkListing =
-                mChunkListingStore.loadProto(packageName);
-
-        // Normally either both protos exist or neither exist, but we correct this just in case.
-        boolean bothPresent = keyValueListing.isPresent() && chunkListing.isPresent();
-        if (!bothPresent) {
-            Slog.d(
-                    TAG,
-                    "Both listing were not present, clearing state, key value="
-                            + keyValueListing.isPresent()
-                            + ", chunk="
-                            + chunkListing.isPresent());
-            deleteListings(packageName);
-            return Optional.empty();
-        }
-
-        return Optional.of(Pair.create(keyValueListing.get(), chunkListing.get()));
-    }
-
-    private void saveListings(
-            String packageName,
-            KeyValueListingProto.KeyValueListing keyValueListing,
-            ChunksMetadataProto.ChunkListing chunkListing) {
-        try {
-            mKeyValueListingStore.saveProto(packageName, keyValueListing);
-            mChunkListingStore.saveProto(packageName, chunkListing);
-        } catch (IOException e) {
-            // If a problem occurred while saving either listing then they may be inconsistent, so
-            // delete
-            // both.
-            Slog.w(TAG, "Unable to save listings, deleting both for consistency", e);
-            deleteListings(packageName);
-        }
-    }
-
-    private void deleteListings(String packageName) {
-        mKeyValueListingStore.deleteProto(packageName);
-        mChunkListingStore.deleteProto(packageName);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTask.java
deleted file mode 100644
index 12b4459..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTask.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import android.app.backup.BackupDataOutput;
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.backup.encryption.FullRestoreDownloader;
-import com.android.server.backup.encryption.chunking.ChunkHasher;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
-import com.android.server.backup.encryption.keys.RestoreKeyFetcher;
-import com.android.server.backup.encryption.kv.DecryptedChunkKvOutput;
-import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import java.io.File;
-import java.io.IOException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.KeyException;
-import java.security.NoSuchAlgorithmException;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.ShortBufferException;
-
-/**
- * Performs a key value restore by downloading the backup set, decrypting it and writing it to the
- * file provided by backup manager.
- */
-public class EncryptedKvRestoreTask {
-    private static final String ENCRYPTED_FILE_NAME = "encrypted_kv";
-
-    private final File mTemporaryFolder;
-    private final ChunkHasher mChunkHasher;
-    private final FullRestoreToFileTask mFullRestoreToFileTask;
-    private final BackupFileDecryptorTask mBackupFileDecryptorTask;
-
-    /** Constructs new instances of the task. */
-    public static class EncryptedKvRestoreTaskFactory {
-        /**
-         * Constructs a new instance.
-         *
-         * <p>Fetches the appropriate secondary key and uses this to unwrap the tertiary key. Stores
-         * temporary files in {@link Context#getFilesDir()}.
-         */
-        public EncryptedKvRestoreTask newInstance(
-                Context context,
-                RecoverableKeyStoreSecondaryKeyManager
-                                .RecoverableKeyStoreSecondaryKeyManagerProvider
-                        recoverableSecondaryKeyManagerProvider,
-                FullRestoreDownloader fullRestoreDownloader,
-                String secondaryKeyAlias,
-                WrappedKeyProto.WrappedKey wrappedTertiaryKey)
-                throws EncryptedRestoreException, NoSuchAlgorithmException, NoSuchPaddingException,
-                        KeyException, InvalidAlgorithmParameterException {
-            SecretKey tertiaryKey =
-                    RestoreKeyFetcher.unwrapTertiaryKey(
-                            recoverableSecondaryKeyManagerProvider,
-                            secondaryKeyAlias,
-                            wrappedTertiaryKey);
-
-            return new EncryptedKvRestoreTask(
-                    context.getFilesDir(),
-                    new ChunkHasher(tertiaryKey),
-                    new FullRestoreToFileTask(fullRestoreDownloader),
-                    new BackupFileDecryptorTask(tertiaryKey));
-        }
-    }
-
-    @VisibleForTesting
-    EncryptedKvRestoreTask(
-            File temporaryFolder,
-            ChunkHasher chunkHasher,
-            FullRestoreToFileTask fullRestoreToFileTask,
-            BackupFileDecryptorTask backupFileDecryptorTask) {
-        checkArgument(
-                temporaryFolder.isDirectory(), "Temporary folder must be an existing directory");
-
-        mTemporaryFolder = temporaryFolder;
-        mChunkHasher = chunkHasher;
-        mFullRestoreToFileTask = fullRestoreToFileTask;
-        mBackupFileDecryptorTask = backupFileDecryptorTask;
-    }
-
-    /**
-     * Runs the restore, writing the pairs in lexicographical order to the given file descriptor.
-     *
-     * <p>This will block for the duration of the restore.
-     *
-     * @throws EncryptedRestoreException if there is a problem decrypting or verifying the backup
-     */
-    public void getRestoreData(ParcelFileDescriptor output)
-            throws IOException, EncryptedRestoreException, BadPaddingException,
-                    InvalidAlgorithmParameterException, NoSuchAlgorithmException,
-                    IllegalBlockSizeException, ShortBufferException, InvalidKeyException {
-        File encryptedFile = new File(mTemporaryFolder, ENCRYPTED_FILE_NAME);
-        try {
-            downloadDecryptAndWriteBackup(encryptedFile, output);
-        } finally {
-            encryptedFile.delete();
-        }
-    }
-
-    private void downloadDecryptAndWriteBackup(File encryptedFile, ParcelFileDescriptor output)
-            throws EncryptedRestoreException, IOException, BadPaddingException, InvalidKeyException,
-                    NoSuchAlgorithmException, IllegalBlockSizeException, ShortBufferException,
-                    InvalidAlgorithmParameterException {
-        mFullRestoreToFileTask.restoreToFile(encryptedFile);
-        DecryptedChunkKvOutput decryptedChunkKvOutput = new DecryptedChunkKvOutput(mChunkHasher);
-        mBackupFileDecryptorTask.decryptFile(encryptedFile, decryptedChunkKvOutput);
-
-        BackupDataOutput backupDataOutput = new BackupDataOutput(output.getFileDescriptor());
-        for (KeyValuePairProto.KeyValuePair pair : decryptedChunkKvOutput.getPairs()) {
-            backupDataOutput.writeEntityHeader(pair.key, pair.value.length);
-            backupDataOutput.writeEntityData(pair.value, pair.value.length);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedRestoreException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedRestoreException.java
deleted file mode 100644
index 487c0d9..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedRestoreException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-/** Wraps any exception related to encryption which occurs during restore. */
-public class EncryptedRestoreException extends Exception {
-    public EncryptedRestoreException(String message) {
-        super(message);
-    }
-
-    public EncryptedRestoreException(Throwable cause) {
-        super(cause);
-    }
-
-    public EncryptedRestoreException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTask.java
deleted file mode 100644
index 82f83f9..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTask.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.backup.encryption.FullRestoreDownloader;
-import com.android.server.backup.encryption.FullRestoreDownloader.FinishType;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Reads a stream from a {@link FullRestoreDownloader} and writes it to a file for consumption by
- * {@link BackupFileDecryptorTask}.
- */
-public class FullRestoreToFileTask {
-    /**
-     * Maximum number of bytes which the framework can request from the full restore data stream in
-     * one call to {@link BackupTransport#getNextFullRestoreDataChunk}.
-     */
-    public static final int MAX_BYTES_FULL_RESTORE_CHUNK = 1024 * 32;
-
-    /** Returned when the end of a backup stream has been reached. */
-    private static final int END_OF_STREAM = -1;
-
-    private final FullRestoreDownloader mFullRestoreDownloader;
-    private final int mBufferSize;
-
-    /**
-     * Constructs a new instance which reads from the given package wrapper, using a buffer of size
-     * {@link #MAX_BYTES_FULL_RESTORE_CHUNK}.
-     */
-    public FullRestoreToFileTask(FullRestoreDownloader fullRestoreDownloader) {
-        this(fullRestoreDownloader, MAX_BYTES_FULL_RESTORE_CHUNK);
-    }
-
-    @VisibleForTesting
-    FullRestoreToFileTask(FullRestoreDownloader fullRestoreDownloader, int bufferSize) {
-        checkArgument(bufferSize > 0, "Buffer must have positive size");
-
-        this.mFullRestoreDownloader = fullRestoreDownloader;
-        this.mBufferSize = bufferSize;
-    }
-
-    /**
-     * Downloads the backup file from the server and writes it to the given file.
-     *
-     * <p>At the end of the download (success or failure), closes the connection and sends a
-     * Clearcut log.
-     */
-    public void restoreToFile(File targetFile) throws IOException {
-        try (BufferedOutputStream outputStream =
-                new BufferedOutputStream(new FileOutputStream(targetFile))) {
-            byte[] buffer = new byte[mBufferSize];
-            int bytesRead = mFullRestoreDownloader.readNextChunk(buffer);
-            while (bytesRead != END_OF_STREAM) {
-                outputStream.write(buffer, /* off=*/ 0, bytesRead);
-                bytesRead = mFullRestoreDownloader.readNextChunk(buffer);
-            }
-
-            outputStream.flush();
-
-            mFullRestoreDownloader.finish(FinishType.FINISHED);
-        } catch (IOException e) {
-            mFullRestoreDownloader.finish(FinishType.TRANSFER_FAILURE);
-            throw e;
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTask.java
deleted file mode 100644
index d436554..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTask.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import android.content.Context;
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.LockScreenRequiredException;
-import android.security.keystore.recovery.RecoveryController;
-import android.util.Slog;
-
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
-
-import java.security.InvalidKeyException;
-import java.security.UnrecoverableKeyException;
-import java.util.Collections;
-import java.util.Optional;
-
-/**
- * Initializes the device for encrypted backup, through generating a secondary key, and setting its
- * alias in the settings.
- *
- * <p>If the device is already initialized, this is a no-op.
- */
-public class InitializeRecoverableSecondaryKeyTask {
-    private static final String TAG = "InitializeRecoverableSecondaryKeyTask";
-
-    private final Context mContext;
-    private final CryptoSettings mCryptoSettings;
-    private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
-    private final CryptoBackupServer mBackupServer;
-
-    /**
-     * A new instance.
-     *
-     * @param cryptoSettings Settings to store the active key alias.
-     * @param secondaryKeyManager Key manager to generate the new active secondary key.
-     * @param backupServer Server with which to sync the active key alias.
-     */
-    public InitializeRecoverableSecondaryKeyTask(
-            Context context,
-            CryptoSettings cryptoSettings,
-            RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager,
-            CryptoBackupServer backupServer) {
-        mContext = context;
-        mCryptoSettings = cryptoSettings;
-        mSecondaryKeyManager = secondaryKeyManager;
-        mBackupServer = backupServer;
-    }
-
-    /**
-     * Initializes the device for encrypted backup, by generating a recoverable secondary key, then
-     * sending that alias to the backup server and saving it in local settings.
-     *
-     * <p>If there is already an active secondary key then does nothing. If the active secondary key
-     * is destroyed then throws {@link InvalidKeyException}.
-     *
-     * <p>If a key rotation is pending and able to finish (i.e., the new key has synced with the
-     * remote trusted hardware module), then it completes the rotation before returning the key.
-     *
-     * @return The active secondary key.
-     * @throws InvalidKeyException if the secondary key is in a bad state.
-     */
-    public RecoverableKeyStoreSecondaryKey run()
-            throws InvalidKeyException, LockScreenRequiredException, UnrecoverableKeyException,
-                    InternalRecoveryServiceException {
-        // Complete any pending key rotations
-        new RotateSecondaryKeyTask(
-                        mContext,
-                        mSecondaryKeyManager,
-                        mBackupServer,
-                        mCryptoSettings,
-                        RecoveryController.getInstance(mContext))
-                .run();
-
-        return runInternal();
-    }
-
-    private RecoverableKeyStoreSecondaryKey runInternal()
-            throws InvalidKeyException, LockScreenRequiredException, UnrecoverableKeyException,
-                    InternalRecoveryServiceException {
-        Optional<RecoverableKeyStoreSecondaryKey> maybeActiveKey = loadFromSetting();
-
-        if (maybeActiveKey.isPresent()) {
-            assertKeyNotDestroyed(maybeActiveKey.get());
-            Slog.d(TAG, "Secondary key already initialized: " + maybeActiveKey.get().getAlias());
-            return maybeActiveKey.get();
-        }
-
-        Slog.v(TAG, "Initializing for crypto: generating a secondary key.");
-        RecoverableKeyStoreSecondaryKey key = mSecondaryKeyManager.generate();
-
-        String alias = key.getAlias();
-        Slog.i(TAG, "Generated new secondary key " + alias);
-
-        // No tertiary keys yet as we are creating a brand new secondary (without rotation).
-        mBackupServer.setActiveSecondaryKeyAlias(alias, /*tertiaryKeys=*/ Collections.emptyMap());
-        Slog.v(TAG, "Successfully synced %s " + alias + " with server.");
-
-        mCryptoSettings.initializeWithKeyAlias(alias);
-        Slog.v(TAG, "Successfully saved " + alias + " as active secondary to disk.");
-
-        return key;
-    }
-
-    private void assertKeyNotDestroyed(RecoverableKeyStoreSecondaryKey key)
-            throws InvalidKeyException {
-        if (key.getStatus(mContext) == RecoverableKeyStoreSecondaryKey.Status.DESTROYED) {
-            throw new InvalidKeyException("Key destroyed: " + key.getAlias());
-        }
-    }
-
-    private Optional<RecoverableKeyStoreSecondaryKey> loadFromSetting()
-            throws InvalidKeyException, UnrecoverableKeyException,
-                    InternalRecoveryServiceException {
-
-        // TODO: b/141856950.
-        if (!mCryptoSettings.getIsInitialized()) {
-            return Optional.empty();
-        }
-
-        Optional<String> maybeAlias = mCryptoSettings.getActiveSecondaryKeyAlias();
-        if (!maybeAlias.isPresent()) {
-            throw new InvalidKeyException(
-                    "Settings said crypto was initialized, but there was no active secondary"
-                            + " alias");
-        }
-
-        String alias = maybeAlias.get();
-
-        Optional<RecoverableKeyStoreSecondaryKey> key;
-        key = mSecondaryKeyManager.get(alias);
-
-        if (!key.isPresent()) {
-            throw new InvalidKeyException(
-                    "Initialized with key but it was not in key store: " + alias);
-        }
-
-        return key;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/KvBackupEncrypter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/KvBackupEncrypter.java
deleted file mode 100644
index d20cd4c..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/KvBackupEncrypter.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.android.internal.util.Preconditions.checkState;
-
-import android.annotation.Nullable;
-import android.app.backup.BackupDataInput;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.ChunkEncryptor;
-import com.android.server.backup.encryption.chunking.ChunkHasher;
-import com.android.server.backup.encryption.chunking.EncryptedChunk;
-import com.android.server.backup.encryption.kv.KeyValueListingBuilder;
-import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
-import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.SecretKey;
-
-/**
- * Reads key value backup data from an input, converts each pair into a chunk and encrypts the
- * chunks.
- *
- * <p>The caller should pass in the key value listing from the previous backup, if there is one.
- * This class emits chunks for both existing and new pairs, using the provided listing to
- * determine the hashes of pairs that already exist. During the backup it computes the new listing,
- * which the caller should store on disk and pass in at the start of the next backup.
- *
- * <p>Also computes the message digest, which is {@code SHA-256(chunk hashes sorted
- * lexicographically)}.
- */
-public class KvBackupEncrypter implements BackupEncrypter {
-    private final BackupDataInput mBackupDataInput;
-
-    private KeyValueListingProto.KeyValueListing mOldKeyValueListing;
-    @Nullable private KeyValueListingBuilder mNewKeyValueListing;
-
-    /**
-     * Constructs a new instance which reads data from the given input.
-     *
-     * <p>By default this performs non-incremental backup, call {@link #setOldKeyValueListing} to
-     * perform incremental backup.
-     */
-    public KvBackupEncrypter(BackupDataInput backupDataInput) {
-        mBackupDataInput = backupDataInput;
-        mOldKeyValueListing = KeyValueListingBuilder.emptyListing();
-    }
-
-    /** Sets the old listing to perform incremental backup against. */
-    public void setOldKeyValueListing(KeyValueListingProto.KeyValueListing oldKeyValueListing) {
-        mOldKeyValueListing = oldKeyValueListing;
-    }
-
-    @Override
-    public Result backup(
-            SecretKey secretKey,
-            @Nullable byte[] unusedFingerprintMixerSalt,
-            Set<ChunkHash> unusedExistingChunks)
-            throws IOException, GeneralSecurityException {
-        ChunkHasher chunkHasher = new ChunkHasher(secretKey);
-        ChunkEncryptor chunkEncryptor = new ChunkEncryptor(secretKey, new SecureRandom());
-        mNewKeyValueListing = new KeyValueListingBuilder();
-        List<ChunkHash> allChunks = new ArrayList<>();
-        List<EncryptedChunk> newChunks = new ArrayList<>();
-
-        Map<String, ChunkHash> existingChunksToReuse = buildPairMap(mOldKeyValueListing);
-
-        while (mBackupDataInput.readNextHeader()) {
-            String key = mBackupDataInput.getKey();
-            Optional<byte[]> value = readEntireValue(mBackupDataInput);
-
-            // As this pair exists in the new backup, we don't need to add it from the previous
-            // backup.
-            existingChunksToReuse.remove(key);
-
-            // If the value is not present then this key has been deleted.
-            if (value.isPresent()) {
-                EncryptedChunk newChunk =
-                        createEncryptedChunk(chunkHasher, chunkEncryptor, key, value.get());
-                allChunks.add(newChunk.key());
-                newChunks.add(newChunk);
-                mNewKeyValueListing.addPair(key, newChunk.key());
-            }
-        }
-
-        allChunks.addAll(existingChunksToReuse.values());
-
-        mNewKeyValueListing.addAll(existingChunksToReuse);
-
-        return new Result(allChunks, newChunks, createMessageDigest(allChunks));
-    }
-
-    /**
-     * Returns a listing containing the pairs in the new backup.
-     *
-     * <p>You must call {@link #backup} first.
-     */
-    public KeyValueListingProto.KeyValueListing getNewKeyValueListing() {
-        checkState(mNewKeyValueListing != null, "Must call backup() first");
-        return mNewKeyValueListing.build();
-    }
-
-    private static Map<String, ChunkHash> buildPairMap(
-            KeyValueListingProto.KeyValueListing listing) {
-        Map<String, ChunkHash> map = new HashMap<>();
-        for (KeyValueListingProto.KeyValueEntry entry : listing.entries) {
-            map.put(entry.key, new ChunkHash(entry.hash));
-        }
-        return map;
-    }
-
-    private EncryptedChunk createEncryptedChunk(
-            ChunkHasher chunkHasher, ChunkEncryptor chunkEncryptor, String key, byte[] value)
-            throws InvalidKeyException, IllegalBlockSizeException {
-        KeyValuePairProto.KeyValuePair pair = new KeyValuePairProto.KeyValuePair();
-        pair.key = key;
-        pair.value = Arrays.copyOf(value, value.length);
-
-        byte[] plaintext = KeyValuePairProto.KeyValuePair.toByteArray(pair);
-        return chunkEncryptor.encrypt(chunkHasher.computeHash(plaintext), plaintext);
-    }
-
-    private static byte[] createMessageDigest(List<ChunkHash> allChunks)
-            throws NoSuchAlgorithmException {
-        MessageDigest messageDigest =
-                MessageDigest.getInstance(BackupEncrypter.MESSAGE_DIGEST_ALGORITHM);
-        // TODO:b/141531271 Extract sorted chunks code to utility class
-        List<ChunkHash> sortedChunks = new ArrayList<>(allChunks);
-        Collections.sort(sortedChunks);
-        for (ChunkHash hash : sortedChunks) {
-            messageDigest.update(hash.getHash());
-        }
-        return messageDigest.digest();
-    }
-
-    private static Optional<byte[]> readEntireValue(BackupDataInput input) throws IOException {
-        // A negative data size indicates that this key should be deleted.
-        if (input.getDataSize() < 0) {
-            return Optional.empty();
-        }
-
-        byte[] value = new byte[input.getDataSize()];
-        int bytesRead = 0;
-        while (bytesRead < value.length) {
-            bytesRead += input.readEntityData(value, bytesRead, value.length - bytesRead);
-        }
-        return Optional.of(value);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MalformedEncryptedFileException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MalformedEncryptedFileException.java
deleted file mode 100644
index 78c370b..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MalformedEncryptedFileException.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-/** Exception thrown when we cannot parse the encrypted backup file. */
-public class MalformedEncryptedFileException extends EncryptedRestoreException {
-    public MalformedEncryptedFileException(String message) {
-        super(message);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MessageDigestMismatchException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MessageDigestMismatchException.java
deleted file mode 100644
index 1e4f43b..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MessageDigestMismatchException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-/**
- * Error thrown if the message digest of the plaintext backup does not match that in the {@link
- * com.android.server.backup.encryption.protos.ChunksMetadataProto.ChunkOrdering}.
- */
-public class MessageDigestMismatchException extends EncryptedRestoreException {
-    public MessageDigestMismatchException(String message) {
-        super(message);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NoActiveSecondaryKeyException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NoActiveSecondaryKeyException.java
deleted file mode 100644
index 72e8a89..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NoActiveSecondaryKeyException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-/**
- * Error thrown if attempting to rotate key when there is no current active secondary key set
- * locally. This means the device needs to re-initialize, asking the backup server what the active
- * secondary key is.
- */
-public class NoActiveSecondaryKeyException extends Exception {
-    public NoActiveSecondaryKeyException(String message) {
-        super(message);
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NonIncrementalBackupRequiredException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NonIncrementalBackupRequiredException.java
deleted file mode 100644
index a3eda7d..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NonIncrementalBackupRequiredException.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-// TODO(141840878): Update documentation.
-/**
- * Exception thrown when the framework provides an incremental backup but the transport requires a
- * non-incremental backup.
- */
-public class NonIncrementalBackupRequiredException extends Exception {}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
deleted file mode 100644
index e5e2c1c..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static android.os.Build.VERSION_CODES.P;
-
-import android.content.Context;
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.RecoveryController;
-import android.util.Slog;
-
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.keys.KeyWrapUtils;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
-import com.android.server.backup.encryption.keys.TertiaryKeyStore;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import java.io.IOException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-
-/**
- * Finishes a rotation for a {@link
- * com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey}.
- */
-public class RotateSecondaryKeyTask {
-    private static final String TAG = "RotateSecondaryKeyTask";
-
-    private final Context mContext;
-    private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
-    private final CryptoBackupServer mBackupServer;
-    private final CryptoSettings mCryptoSettings;
-    private final RecoveryController mRecoveryController;
-
-    /**
-     * A new instance.
-     *
-     * @param secondaryKeyManager For loading the currently active and next secondary key.
-     * @param backupServer For loading and storing tertiary keys and for setting active secondary
-     *     key.
-     * @param cryptoSettings For checking the stored aliases for the next and active key.
-     * @param recoveryController For communicating with the Framework apis.
-     */
-    public RotateSecondaryKeyTask(
-            Context context,
-            RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager,
-            CryptoBackupServer backupServer,
-            CryptoSettings cryptoSettings,
-            RecoveryController recoveryController) {
-        mContext = context;
-        mSecondaryKeyManager = Objects.requireNonNull(secondaryKeyManager);
-        mCryptoSettings = Objects.requireNonNull(cryptoSettings);
-        mBackupServer = Objects.requireNonNull(backupServer);
-        mRecoveryController = Objects.requireNonNull(recoveryController);
-    }
-
-    /** Runs the task. */
-    public void run() {
-        // Never run more than one of these at the same time.
-        synchronized (RotateSecondaryKeyTask.class) {
-            runInternal();
-        }
-    }
-
-    private void runInternal() {
-        Optional<RecoverableKeyStoreSecondaryKey> maybeNextKey;
-        try {
-            maybeNextKey = getNextKey();
-        } catch (Exception e) {
-            Slog.e(TAG, "Error checking for next key", e);
-            return;
-        }
-
-        if (!maybeNextKey.isPresent()) {
-            Slog.d(TAG, "No secondary key rotation task pending. Exiting.");
-            return;
-        }
-
-        RecoverableKeyStoreSecondaryKey nextKey = maybeNextKey.get();
-        boolean isReady;
-        try {
-            isReady = isSecondaryKeyRotationReady(nextKey);
-        } catch (InternalRecoveryServiceException e) {
-            Slog.e(TAG, "Error encountered checking whether next secondary key is synced", e);
-            return;
-        }
-
-        if (!isReady) {
-            return;
-        }
-
-        try {
-            rotateToKey(nextKey);
-        } catch (Exception e) {
-            Slog.e(TAG, "Error trying to rotate to new secondary key", e);
-        }
-    }
-
-    private Optional<RecoverableKeyStoreSecondaryKey> getNextKey()
-            throws InternalRecoveryServiceException, UnrecoverableKeyException {
-        Optional<String> maybeNextAlias = mCryptoSettings.getNextSecondaryKeyAlias();
-        if (!maybeNextAlias.isPresent()) {
-            return Optional.empty();
-        }
-        return mSecondaryKeyManager.get(maybeNextAlias.get());
-    }
-
-    private boolean isSecondaryKeyRotationReady(RecoverableKeyStoreSecondaryKey nextKey)
-            throws InternalRecoveryServiceException {
-        String nextAlias = nextKey.getAlias();
-        Slog.i(TAG, "Key rotation to " + nextAlias + " is pending. Checking key sync status.");
-        int status = mRecoveryController.getRecoveryStatus(nextAlias);
-
-        if (status == RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE) {
-            Slog.e(
-                    TAG,
-                    "Permanent failure to sync " + nextAlias + ". Cannot possibly rotate to it.");
-            mCryptoSettings.removeNextSecondaryKeyAlias();
-            return false;
-        }
-
-        if (status == RecoveryController.RECOVERY_STATUS_SYNCED) {
-            Slog.i(TAG, "Secondary key " + nextAlias + " has now synced! Commencing rotation.");
-        } else {
-            Slog.i(TAG, "Sync still pending for " + nextAlias);
-        }
-        return status == RecoveryController.RECOVERY_STATUS_SYNCED;
-    }
-
-    /**
-     * @throws ActiveSecondaryNotInKeychainException if the currently active secondary key is not in
-     *     the keychain.
-     * @throws IOException if there is an IO issue communicating with the server or loading from
-     *     disk.
-     * @throws NoActiveSecondaryKeyException if there is no active key set.
-     * @throws IllegalBlockSizeException if there is an issue decrypting a tertiary key.
-     * @throws InvalidKeyException if any of the secondary keys cannot be used for wrapping or
-     *     unwrapping tertiary keys.
-     */
-    private void rotateToKey(RecoverableKeyStoreSecondaryKey newSecondaryKey)
-            throws ActiveSecondaryNotInKeychainException, IOException,
-                    NoActiveSecondaryKeyException, IllegalBlockSizeException, InvalidKeyException,
-                    InternalRecoveryServiceException, UnrecoverableKeyException,
-                    InvalidAlgorithmParameterException, NoSuchAlgorithmException,
-                    NoSuchPaddingException {
-        RecoverableKeyStoreSecondaryKey activeSecondaryKey = getActiveSecondaryKey();
-        String activeSecondaryKeyAlias = activeSecondaryKey.getAlias();
-        String newSecondaryKeyAlias = newSecondaryKey.getAlias();
-        if (newSecondaryKeyAlias.equals(activeSecondaryKeyAlias)) {
-            Slog.i(TAG, activeSecondaryKeyAlias + " was already the active alias.");
-            return;
-        }
-
-        TertiaryKeyStore tertiaryKeyStore =
-                TertiaryKeyStore.newInstance(mContext, activeSecondaryKey);
-        Map<String, SecretKey> tertiaryKeys = tertiaryKeyStore.getAll();
-
-        if (tertiaryKeys.isEmpty()) {
-            Slog.i(
-                    TAG,
-                    "No tertiary keys for " + activeSecondaryKeyAlias + ". No need to rewrap. ");
-            mBackupServer.setActiveSecondaryKeyAlias(
-                    newSecondaryKeyAlias, /*tertiaryKeys=*/ Collections.emptyMap());
-        } else {
-            Map<String, WrappedKeyProto.WrappedKey> rewrappedTertiaryKeys =
-                    rewrapAll(newSecondaryKey, tertiaryKeys);
-            TertiaryKeyStore.newInstance(mContext, newSecondaryKey).putAll(rewrappedTertiaryKeys);
-            Slog.i(
-                    TAG,
-                    "Successfully rewrapped " + rewrappedTertiaryKeys.size() + " tertiary keys");
-            mBackupServer.setActiveSecondaryKeyAlias(newSecondaryKeyAlias, rewrappedTertiaryKeys);
-            Slog.i(
-                    TAG,
-                    "Successfully uploaded new set of tertiary keys to "
-                            + newSecondaryKeyAlias
-                            + " alias");
-        }
-
-        mCryptoSettings.setActiveSecondaryKeyAlias(newSecondaryKeyAlias);
-        mCryptoSettings.removeNextSecondaryKeyAlias();
-        try {
-            mRecoveryController.removeKey(activeSecondaryKeyAlias);
-        } catch (InternalRecoveryServiceException e) {
-            Slog.e(TAG, "Error removing old secondary key from RecoverableKeyStoreLoader", e);
-        }
-    }
-
-    private RecoverableKeyStoreSecondaryKey getActiveSecondaryKey()
-            throws NoActiveSecondaryKeyException, ActiveSecondaryNotInKeychainException,
-                    InternalRecoveryServiceException, UnrecoverableKeyException {
-
-        Optional<String> activeSecondaryAlias = mCryptoSettings.getActiveSecondaryKeyAlias();
-
-        if (!activeSecondaryAlias.isPresent()) {
-            Slog.i(
-                    TAG,
-                    "Was asked to rotate secondary key, but local config did not have a secondary "
-                            + "key alias set.");
-            throw new NoActiveSecondaryKeyException("No local active secondary key set.");
-        }
-
-        String activeSecondaryKeyAlias = activeSecondaryAlias.get();
-        Optional<RecoverableKeyStoreSecondaryKey> secondaryKey =
-                mSecondaryKeyManager.get(activeSecondaryKeyAlias);
-
-        if (!secondaryKey.isPresent()) {
-            throw new ActiveSecondaryNotInKeychainException(
-                    String.format(
-                            Locale.US,
-                            "Had local active recoverable key alias of %s but key was not in"
-                                + " user's keychain.",
-                            activeSecondaryKeyAlias));
-        }
-
-        return secondaryKey.get();
-    }
-
-    /**
-     * Rewraps all the tertiary keys.
-     *
-     * @param newSecondaryKey The secondary key with which to rewrap the tertiaries.
-     * @param tertiaryKeys The tertiary keys, by package name.
-     * @return The newly wrapped tertiary keys, by package name.
-     * @throws InvalidKeyException if any key is unusable.
-     * @throws IllegalBlockSizeException if could not decrypt.
-     */
-    private Map<String, WrappedKeyProto.WrappedKey> rewrapAll(
-            RecoverableKeyStoreSecondaryKey newSecondaryKey, Map<String, SecretKey> tertiaryKeys)
-            throws InvalidKeyException, IllegalBlockSizeException, NoSuchPaddingException,
-                    NoSuchAlgorithmException {
-        Map<String, WrappedKeyProto.WrappedKey> wrappedKeys = new HashMap<>();
-
-        for (String packageName : tertiaryKeys.keySet()) {
-            SecretKey tertiaryKey = tertiaryKeys.get(packageName);
-            wrappedKeys.put(
-                    packageName, KeyWrapUtils.wrap(newSecondaryKey.getSecretKey(), tertiaryKey));
-        }
-
-        return wrappedKeys;
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/SizeQuotaExceededException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/SizeQuotaExceededException.java
deleted file mode 100644
index 515db86..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/SizeQuotaExceededException.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-/** Exception thrown when aa backup has exceeded the space allowed for that user */
-public class SizeQuotaExceededException extends RuntimeException {
-    public SizeQuotaExceededException() {
-        super("Backup size quota exceeded.");
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
deleted file mode 100644
index 81169e2..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.LockScreenRequiredException;
-import android.util.Slog;
-
-import com.android.internal.util.Preconditions;
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
-
-import java.security.UnrecoverableKeyException;
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * Starts rotating to a new secondary key. Cannot complete until the screen is unlocked and the new
- * key is synced.
- */
-public class StartSecondaryKeyRotationTask {
-    private static final String TAG = "BE-StSecondaryKeyRotTsk";
-
-    private final CryptoSettings mCryptoSettings;
-    private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
-
-    public StartSecondaryKeyRotationTask(
-            CryptoSettings cryptoSettings,
-            RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager) {
-        mCryptoSettings = Objects.requireNonNull(cryptoSettings);
-        mSecondaryKeyManager = Objects.requireNonNull(secondaryKeyManager);
-    }
-
-    /** Begin the key rotation */
-    public void run() {
-        Slog.i(TAG, "Attempting to initiate a secondary key rotation.");
-
-        Optional<String> maybeCurrentAlias = mCryptoSettings.getActiveSecondaryKeyAlias();
-        if (!maybeCurrentAlias.isPresent()) {
-            Slog.w(TAG, "No active current alias. Cannot trigger a secondary rotation.");
-            return;
-        }
-        String currentAlias = maybeCurrentAlias.get();
-
-        Optional<String> maybeNextAlias = mCryptoSettings.getNextSecondaryKeyAlias();
-        if (maybeNextAlias.isPresent()) {
-            String nextAlias = maybeNextAlias.get();
-            if (nextAlias.equals(currentAlias)) {
-                // Shouldn't be possible, but guard against accidentally deleting the active key.
-                Slog.e(TAG, "Was already trying to rotate to what is already the active key.");
-            } else {
-                Slog.w(TAG, "Was already rotating to another key. Cancelling that.");
-                try {
-                    mSecondaryKeyManager.remove(nextAlias);
-                } catch (Exception e) {
-                    Slog.wtf(TAG, "Could not remove old key", e);
-                }
-            }
-            mCryptoSettings.removeNextSecondaryKeyAlias();
-        }
-
-        RecoverableKeyStoreSecondaryKey newSecondaryKey;
-        try {
-            newSecondaryKey = mSecondaryKeyManager.generate();
-        } catch (LockScreenRequiredException e) {
-            Slog.e(TAG, "No lock screen is set - cannot generate a new key to rotate to.", e);
-            return;
-        } catch (InternalRecoveryServiceException e) {
-            Slog.e(TAG, "Internal error in Recovery Controller, failed to rotate key.", e);
-            return;
-        } catch (UnrecoverableKeyException e) {
-            Slog.e(TAG, "Failed to get key after generating, failed to rotate", e);
-            return;
-        }
-
-        String alias = newSecondaryKey.getAlias();
-        Slog.i(TAG, "Generated a new secondary key with alias '" + alias + "'.");
-        try {
-            mCryptoSettings.setNextSecondaryAlias(alias);
-            Slog.i(TAG, "Successfully set '" + alias + "' as next key to rotate to");
-        } catch (IllegalArgumentException e) {
-            Slog.e(TAG, "Unexpected error setting next alias", e);
-            try {
-                mSecondaryKeyManager.remove(alias);
-            } catch (Exception err) {
-                Slog.wtf(TAG, "Failed to remove generated key after encountering error", err);
-            }
-        }
-    }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/UnsupportedEncryptedFileException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/UnsupportedEncryptedFileException.java
deleted file mode 100644
index 9a97e38..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/UnsupportedEncryptedFileException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-/**
- * Thrown when the backup file provided by the server uses encryption algorithms this version of
- * backup does not support. This could happen if the backup was created with a newer version of the
- * code.
- */
-public class UnsupportedEncryptedFileException extends EncryptedRestoreException {
-    public UnsupportedEncryptedFileException(String message) {
-        super(message);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric-integration/Android.bp b/packages/BackupEncryption/test/robolectric-integration/Android.bp
deleted file mode 100644
index c842e42..0000000
--- a/packages/BackupEncryption/test/robolectric-integration/Android.bp
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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 {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_robolectric_test {
-    name: "BackupEncryptionRoboIntegTests",
-    srcs: [
-        "src/**/*.java",
-    ],
-    java_resource_dirs: ["config"],
-    libs: [
-        "backup-encryption-protos",
-        "platform-test-annotations",
-        "testng",
-        "truth-prebuilt",
-        "BackupEncryptionRoboTests",
-    ],
-    static_libs: [
-        "androidx.test.core",
-        "androidx.test.runner",
-        "androidx.test.rules",
-    ],
-    instrumentation_for: "BackupEncryption",
-}
diff --git a/packages/BackupEncryption/test/robolectric-integration/AndroidManifest.xml b/packages/BackupEncryption/test/robolectric-integration/AndroidManifest.xml
deleted file mode 100644
index c3930cc..0000000
--- a/packages/BackupEncryption/test/robolectric-integration/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  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.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          coreApp="true"
-          package="com.android.server.backup.encryption.robointeg">
-
-    <application/>
-
-</manifest>
diff --git a/packages/BackupEncryption/test/robolectric-integration/config/robolectric.properties b/packages/BackupEncryption/test/robolectric-integration/config/robolectric.properties
deleted file mode 100644
index 26fceb3..0000000
--- a/packages/BackupEncryption/test/robolectric-integration/config/robolectric.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# 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.
-#
-
-sdk=NEWEST_SDK
diff --git a/packages/BackupEncryption/test/robolectric-integration/src/com/android/server/backup/encryption/RoundTripTest.java b/packages/BackupEncryption/test/robolectric-integration/src/com/android/server/backup/encryption/RoundTripTest.java
deleted file mode 100644
index a432d91..0000000
--- a/packages/BackupEncryption/test/robolectric-integration/src/com/android/server/backup/encryption/RoundTripTest.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * 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.encryption;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.RecoveryController;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.keys.KeyWrapUtils;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
-import com.android.server.backup.encryption.keys.TertiaryKeyManager;
-import com.android.server.backup.encryption.keys.TertiaryKeyRotationScheduler;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-import com.android.server.backup.encryption.tasks.EncryptedFullBackupTask;
-import com.android.server.backup.encryption.tasks.EncryptedFullRestoreTask;
-import com.android.server.backup.encryption.tasks.EncryptedKvBackupTask;
-import com.android.server.backup.encryption.tasks.EncryptedKvRestoreTask;
-import com.android.server.testing.shadows.DataEntity;
-import com.android.server.testing.shadows.ShadowBackupDataInput;
-import com.android.server.testing.shadows.ShadowBackupDataOutput;
-import com.android.server.testing.shadows.ShadowRecoveryController;
-
-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.annotation.Config;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Optional;
-import java.util.Map;
-import java.util.Set;
-
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.KeyGenerator;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-
-@Config(
-        shadows = {
-            ShadowBackupDataInput.class,
-            ShadowBackupDataOutput.class,
-            ShadowRecoveryController.class
-        })
-@RunWith(RobolectricTestRunner.class)
-public class RoundTripTest {
-    private static final DataEntity[] KEY_VALUE_DATA = {
-        new DataEntity("test_key_1", "test_value_1"),
-        new DataEntity("test_key_2", "test_value_2"),
-        new DataEntity("test_key_3", "test_value_3")
-    };
-
-    /** Amount of data we want to round trip in this test */
-    private static final int TEST_DATA_SIZE = 1024 * 1024; // 1MB
-
-    /** Buffer size used when reading data from the restore task */
-    private static final int READ_BUFFER_SIZE = 1024; // 1024 byte buffer.
-
-    /** Key parameters used for the secondary encryption key */
-    private static final String KEY_ALGORITHM = "AES";
-
-    private static final int KEY_SIZE_BITS = 256;
-
-    /** Package name for our test package */
-    private static final String TEST_PACKAGE_NAME = "com.android.backup.test";
-
-    /** The name we use to refer to our secondary key */
-    private static final String TEST_KEY_ALIAS = "test/backup/KEY_ALIAS";
-
-    /** Original data used for comparison after round trip */
-    private final byte[] mOriginalData = new byte[TEST_DATA_SIZE];
-
-    /** App context, used to store the key data and chunk listings */
-    private Context mContext;
-
-    /** The secondary key we're using for the test */
-    private RecoverableKeyStoreSecondaryKey mSecondaryKey;
-
-    /** Source of random material which is considered non-predictable in its' generation */
-    private final SecureRandom mSecureRandom = new SecureRandom();
-
-    private RecoverableKeyStoreSecondaryKeyManager.RecoverableKeyStoreSecondaryKeyManagerProvider
-            mSecondaryKeyManagerProvider;
-    private DummyServer mDummyServer;
-    private RecoveryController mRecoveryController;
-
-    @Mock private ParcelFileDescriptor mParcelFileDescriptor;
-
-    @Before
-    public void setUp() throws NoSuchAlgorithmException, InternalRecoveryServiceException {
-        MockitoAnnotations.initMocks(this);
-
-        ShadowBackupDataInput.reset();
-        ShadowBackupDataOutput.reset();
-
-        mContext = ApplicationProvider.getApplicationContext();
-        mSecondaryKey = new RecoverableKeyStoreSecondaryKey(TEST_KEY_ALIAS, generateAesKey());
-        mDummyServer = new DummyServer();
-        mSecondaryKeyManagerProvider =
-                () ->
-                        new RecoverableKeyStoreSecondaryKeyManager(
-                                RecoveryController.getInstance(mContext), mSecureRandom);
-
-        fillBuffer(mOriginalData);
-    }
-
-    @Test
-    public void testFull_nonIncrementalBackupAndRestoreAreSuccessful() throws Exception {
-        byte[] backupData = performFullBackup(mOriginalData);
-        assertThat(backupData).isNotEqualTo(mOriginalData);
-        byte[] restoredData = performFullRestore(backupData);
-        assertThat(restoredData).isEqualTo(mOriginalData);
-    }
-
-    @Test
-    public void testKeyValue_nonIncrementalBackupAndRestoreAreSuccessful() throws Exception {
-        byte[] backupData = performNonIncrementalKeyValueBackup(KEY_VALUE_DATA);
-
-        // Get the secondary key used to do backup.
-        Optional<RecoverableKeyStoreSecondaryKey> secondaryKey =
-                mSecondaryKeyManagerProvider.get().get(mDummyServer.mSecondaryKeyAlias);
-        assertThat(secondaryKey.isPresent()).isTrue();
-
-        Set<DataEntity> restoredData = performKeyValueRestore(backupData, secondaryKey.get());
-
-        assertThat(restoredData).containsExactly(KEY_VALUE_DATA).inOrder();
-    }
-
-    /** Perform a key/value backup and return the backed-up representation of the data */
-    private byte[] performNonIncrementalKeyValueBackup(DataEntity[] backupData)
-            throws Exception {
-        // Populate test key/value data.
-        for (DataEntity entity : backupData) {
-            ShadowBackupDataInput.addEntity(entity);
-        }
-
-        EncryptedKvBackupTask.EncryptedKvBackupTaskFactory backupTaskFactory =
-                new EncryptedKvBackupTask.EncryptedKvBackupTaskFactory();
-        EncryptedKvBackupTask backupTask =
-                backupTaskFactory.newInstance(
-                        mContext,
-                        mSecureRandom,
-                        mDummyServer,
-                        CryptoSettings.getInstance(mContext),
-                        mSecondaryKeyManagerProvider,
-                        mParcelFileDescriptor,
-                        TEST_PACKAGE_NAME);
-
-        backupTask.performBackup(/* incremental */ false);
-
-        return mDummyServer.mStoredData;
-    }
-
-    /** Perform a full backup and return the backed-up representation of the data */
-    private byte[] performFullBackup(byte[] backupData) throws Exception {
-        DummyServer dummyServer = new DummyServer();
-        EncryptedFullBackupTask backupTask =
-                EncryptedFullBackupTask.newInstance(
-                        mContext,
-                        dummyServer,
-                        mSecureRandom,
-                        mSecondaryKey,
-                        TEST_PACKAGE_NAME,
-                        new ByteArrayInputStream(backupData));
-        backupTask.call();
-        return dummyServer.mStoredData;
-    }
-
-    private Set<DataEntity> performKeyValueRestore(
-            byte[] backupData, RecoverableKeyStoreSecondaryKey secondaryKey) throws Exception {
-        EncryptedKvRestoreTask.EncryptedKvRestoreTaskFactory restoreTaskFactory =
-                new EncryptedKvRestoreTask.EncryptedKvRestoreTaskFactory();
-        EncryptedKvRestoreTask restoreTask =
-                restoreTaskFactory.newInstance(
-                        mContext,
-                        mSecondaryKeyManagerProvider,
-                        new FakeFullRestoreDownloader(backupData),
-                        secondaryKey.getAlias(),
-                        KeyWrapUtils.wrap(
-                                secondaryKey.getSecretKey(), getTertiaryKey(secondaryKey)));
-        restoreTask.getRestoreData(mParcelFileDescriptor);
-        return ShadowBackupDataOutput.getEntities();
-    }
-
-    /** Perform a full restore and return the bytes obtained from the restore process */
-    private byte[] performFullRestore(byte[] backupData)
-            throws IOException, NoSuchAlgorithmException, NoSuchPaddingException,
-                    InvalidAlgorithmParameterException, InvalidKeyException,
-                    IllegalBlockSizeException {
-        ByteArrayOutputStream decryptedOutput = new ByteArrayOutputStream();
-
-        EncryptedFullRestoreTask restoreTask =
-                EncryptedFullRestoreTask.newInstance(
-                        mContext,
-                        new FakeFullRestoreDownloader(backupData),
-                        getTertiaryKey(mSecondaryKey));
-
-        byte[] buffer = new byte[READ_BUFFER_SIZE];
-        int bytesRead = restoreTask.readNextChunk(buffer);
-        while (bytesRead != -1) {
-            decryptedOutput.write(buffer, 0, bytesRead);
-            bytesRead = restoreTask.readNextChunk(buffer);
-        }
-
-        return decryptedOutput.toByteArray();
-    }
-
-    /** Get the tertiary key for our test package from the key manager */
-    private SecretKey getTertiaryKey(RecoverableKeyStoreSecondaryKey secondaryKey)
-            throws IllegalBlockSizeException, InvalidAlgorithmParameterException,
-                    NoSuchAlgorithmException, IOException, NoSuchPaddingException,
-                    InvalidKeyException {
-        TertiaryKeyManager tertiaryKeyManager =
-                new TertiaryKeyManager(
-                        mContext,
-                        mSecureRandom,
-                        TertiaryKeyRotationScheduler.getInstance(mContext),
-                        secondaryKey,
-                        TEST_PACKAGE_NAME);
-        return tertiaryKeyManager.getKey();
-    }
-
-    /** Fill a buffer with data in a predictable way */
-    private void fillBuffer(byte[] buffer) {
-        byte loopingCounter = 0;
-        for (int i = 0; i < buffer.length; i++) {
-            buffer[i] = loopingCounter;
-            loopingCounter++;
-        }
-    }
-
-    /** Generate a new, random, AES key */
-    public static SecretKey generateAesKey() throws NoSuchAlgorithmException {
-        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
-        keyGenerator.init(KEY_SIZE_BITS);
-        return keyGenerator.generateKey();
-    }
-
-    /**
-     * Dummy backup data endpoint. This stores the data so we can use it in subsequent test steps.
-     */
-    private static class DummyServer implements CryptoBackupServer {
-        private static final String DUMMY_DOC_ID = "DummyDoc";
-
-        byte[] mStoredData = null;
-        String mSecondaryKeyAlias;
-
-        @Override
-        public String uploadIncrementalBackup(
-                String packageName,
-                String oldDocId,
-                byte[] diffScript,
-                WrappedKeyProto.WrappedKey tertiaryKey) {
-            throw new RuntimeException("Not Implemented");
-        }
-
-        @Override
-        public String uploadNonIncrementalBackup(
-                String packageName, byte[] data, WrappedKeyProto.WrappedKey tertiaryKey) {
-            assertThat(packageName).isEqualTo(TEST_PACKAGE_NAME);
-            mStoredData = data;
-            return DUMMY_DOC_ID;
-        }
-
-        @Override
-        public void setActiveSecondaryKeyAlias(
-                String keyAlias, Map<String, WrappedKeyProto.WrappedKey> tertiaryKeys) {
-            mSecondaryKeyAlias = keyAlias;
-        }
-    }
-
-    /** Fake package wrapper which returns data from a byte array. */
-    private static class FakeFullRestoreDownloader extends FullRestoreDownloader {
-        private final ByteArrayInputStream mData;
-
-        FakeFullRestoreDownloader(byte[] data) {
-            // We override all methods of the superclass, so it does not require any collaborators.
-            super();
-            mData = new ByteArrayInputStream(data);
-        }
-
-        @Override
-        public int readNextChunk(byte[] buffer) throws IOException {
-            return mData.read(buffer);
-        }
-
-        @Override
-        public void finish(FinishType finishType) {
-            // Do nothing.
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/Android.bp b/packages/BackupEncryption/test/robolectric/Android.bp
deleted file mode 100644
index 7665d8f..0000000
--- a/packages/BackupEncryption/test/robolectric/Android.bp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2018 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 {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_robolectric_test {
-    name: "BackupEncryptionRoboTests",
-    srcs: [
-        "src/**/*.java",
-//        ":FrameworksServicesRoboShadows",
-    ],
-    java_resource_dirs: ["config"],
-    libs: [
-        "backup-encryption-protos",
-        "platform-test-annotations",
-        "testng",
-        "truth-prebuilt",
-    ],
-    static_libs: [
-        "androidx.test.core",
-        "androidx.test.runner",
-        "androidx.test.rules",
-    ],
-    instrumentation_for: "BackupEncryption",
-}
-
-filegroup {
-    name: "BackupEncryptionRoboShadows",
-    srcs: ["src/com/android/server/testing/shadows/**/*.java"],
-}
diff --git a/packages/BackupEncryption/test/robolectric/AndroidManifest.xml b/packages/BackupEncryption/test/robolectric/AndroidManifest.xml
deleted file mode 100644
index ae5cdd9..0000000
--- a/packages/BackupEncryption/test/robolectric/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  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.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          coreApp="true"
-          package="com.android.server.backup.encryption.robotests">
-
-    <application/>
-
-</manifest>
diff --git a/packages/BackupEncryption/test/robolectric/config/robolectric.properties b/packages/BackupEncryption/test/robolectric/config/robolectric.properties
deleted file mode 100644
index 26fceb3..0000000
--- a/packages/BackupEncryption/test/robolectric/config/robolectric.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# 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.
-#
-
-sdk=NEWEST_SDK
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/CryptoSettingsTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/CryptoSettingsTest.java
deleted file mode 100644
index 979b3d5..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/CryptoSettingsTest.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * 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.encryption;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.app.Application;
-import android.security.keystore.recovery.RecoveryController;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.testing.shadows.ShadowRecoveryController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-import java.util.Optional;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowRecoveryController.class)
-public class CryptoSettingsTest {
-
-    private static final String TEST_KEY_ALIAS =
-            "com.android.server.backup.encryption/keystore/08120c326b928ff34c73b9c58581da63";
-
-    private CryptoSettings mCryptoSettings;
-    private Application mApplication;
-
-    @Before
-    public void setUp() {
-        ShadowRecoveryController.reset();
-
-        mApplication = ApplicationProvider.getApplicationContext();
-        mCryptoSettings = CryptoSettings.getInstanceForTesting(mApplication);
-    }
-
-    @Test
-    public void getActiveSecondaryAlias_isInitiallyAbsent() {
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().isPresent()).isFalse();
-    }
-
-    @Test
-    public void getActiveSecondaryAlias_returnsAliasIfKeyIsInRecoveryController() throws Exception {
-        setAliasIsInRecoveryController(TEST_KEY_ALIAS);
-        mCryptoSettings.setActiveSecondaryKeyAlias(TEST_KEY_ALIAS);
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get()).isEqualTo(TEST_KEY_ALIAS);
-    }
-
-    @Test
-    public void getNextSecondaryAlias_isInitiallyAbsent() {
-        assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isFalse();
-    }
-
-    @Test
-    public void getNextSecondaryAlias_returnsAliasIfKeyIsInRecoveryController() throws Exception {
-        setAliasIsInRecoveryController(TEST_KEY_ALIAS);
-        mCryptoSettings.setNextSecondaryAlias(TEST_KEY_ALIAS);
-        assertThat(mCryptoSettings.getNextSecondaryKeyAlias().get()).isEqualTo(TEST_KEY_ALIAS);
-    }
-
-    @Test
-    public void isInitialized_isInitiallyFalse() {
-        assertThat(mCryptoSettings.getIsInitialized()).isFalse();
-    }
-
-    @Test
-    public void setActiveSecondaryAlias_throwsIfKeyIsNotInRecoveryController() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> mCryptoSettings.setActiveSecondaryKeyAlias(TEST_KEY_ALIAS));
-    }
-
-    @Test
-    public void setNextSecondaryAlias_inRecoveryController_setsAlias() throws Exception {
-        setAliasIsInRecoveryController(TEST_KEY_ALIAS);
-
-        mCryptoSettings.setNextSecondaryAlias(TEST_KEY_ALIAS);
-
-        assertThat(mCryptoSettings.getNextSecondaryKeyAlias().get()).isEqualTo(TEST_KEY_ALIAS);
-    }
-
-    @Test
-    public void setNextSecondaryAlias_throwsIfKeyIsNotInRecoveryController() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> mCryptoSettings.setNextSecondaryAlias(TEST_KEY_ALIAS));
-    }
-
-    @Test
-    public void removeNextSecondaryAlias_removesIt() throws Exception {
-        setAliasIsInRecoveryController(TEST_KEY_ALIAS);
-        mCryptoSettings.setNextSecondaryAlias(TEST_KEY_ALIAS);
-
-        mCryptoSettings.removeNextSecondaryKeyAlias();
-
-        assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isFalse();
-    }
-
-    @Test
-    public void initializeWithKeyAlias_setsAsInitialized() throws Exception {
-        setAliasIsInRecoveryController(TEST_KEY_ALIAS);
-        mCryptoSettings.initializeWithKeyAlias(TEST_KEY_ALIAS);
-        assertThat(mCryptoSettings.getIsInitialized()).isTrue();
-    }
-
-    @Test
-    public void initializeWithKeyAlias_setsActiveAlias() throws Exception {
-        setAliasIsInRecoveryController(TEST_KEY_ALIAS);
-        mCryptoSettings.initializeWithKeyAlias(TEST_KEY_ALIAS);
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get()).isEqualTo(TEST_KEY_ALIAS);
-    }
-
-    @Test
-    public void initializeWithKeyAlias_throwsIfKeyIsNotInRecoveryController() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> mCryptoSettings.initializeWithKeyAlias(TEST_KEY_ALIAS));
-    }
-
-    @Test
-    public void initializeWithKeyAlias_throwsIfAlreadyInitialized() throws Exception {
-        setAliasIsInRecoveryController(TEST_KEY_ALIAS);
-        mCryptoSettings.initializeWithKeyAlias(TEST_KEY_ALIAS);
-
-        assertThrows(
-                IllegalStateException.class,
-                () -> mCryptoSettings.initializeWithKeyAlias(TEST_KEY_ALIAS));
-    }
-
-    @Test
-    public void getSecondaryLastRotated_returnsEmptyInitially() {
-        assertThat(mCryptoSettings.getSecondaryLastRotated()).isEqualTo(Optional.empty());
-    }
-
-    @Test
-    public void getSecondaryLastRotated_returnsTimestampAfterItIsSet() {
-        long timestamp = 1000001;
-
-        mCryptoSettings.setSecondaryLastRotated(timestamp);
-
-        assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(timestamp);
-    }
-
-    @Test
-    public void getAncestralSecondaryKeyVersion_notSet_returnsOptionalAbsent() {
-        assertThat(mCryptoSettings.getAncestralSecondaryKeyVersion().isPresent()).isFalse();
-    }
-
-    @Test
-    public void getAncestralSecondaryKeyVersion_isSet_returnsSetValue() {
-        String secondaryKeyVersion = "some_secondary_key";
-        mCryptoSettings.setAncestralSecondaryKeyVersion(secondaryKeyVersion);
-
-        assertThat(mCryptoSettings.getAncestralSecondaryKeyVersion().get())
-                .isEqualTo(secondaryKeyVersion);
-    }
-
-    @Test
-    public void getAncestralSecondaryKeyVersion_isSetMultipleTimes_returnsLastSetValue() {
-        String secondaryKeyVersion1 = "some_secondary_key";
-        String secondaryKeyVersion2 = "another_secondary_key";
-        mCryptoSettings.setAncestralSecondaryKeyVersion(secondaryKeyVersion1);
-        mCryptoSettings.setAncestralSecondaryKeyVersion(secondaryKeyVersion2);
-
-        assertThat(mCryptoSettings.getAncestralSecondaryKeyVersion().get())
-                .isEqualTo(secondaryKeyVersion2);
-    }
-
-    @Test
-    public void clearAllSettingsForBackup_clearsStateForBackup() throws Exception {
-        String key1 = "key1";
-        String key2 = "key2";
-        String ancestralKey = "ancestral_key";
-        setAliasIsInRecoveryController(key1);
-        setAliasIsInRecoveryController(key2);
-        mCryptoSettings.setActiveSecondaryKeyAlias(key1);
-        mCryptoSettings.setNextSecondaryAlias(key2);
-        mCryptoSettings.setSecondaryLastRotated(100001);
-        mCryptoSettings.setAncestralSecondaryKeyVersion(ancestralKey);
-
-        mCryptoSettings.clearAllSettingsForBackup();
-
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().isPresent()).isFalse();
-        assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isFalse();
-        assertThat(mCryptoSettings.getSecondaryLastRotated().isPresent()).isFalse();
-        assertThat(mCryptoSettings.getAncestralSecondaryKeyVersion().get()).isEqualTo(ancestralKey);
-    }
-
-    private void setAliasIsInRecoveryController(String alias) throws Exception {
-        RecoveryController recoveryController = RecoveryController.getInstance(mApplication);
-        recoveryController.generateKey(alias);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java
deleted file mode 100644
index a95e87e..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.encryption;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-@RunWith(RobolectricTestRunner.class)
-public class StreamUtilsTest {
-    private static final int SOURCE_DATA_SIZE = 64;
-
-    private byte[] mSourceData;
-
-    private InputStream mSource;
-    private ByteArrayOutputStream mDestination;
-
-    @Before
-    public void setUp() {
-        mSourceData = new byte[SOURCE_DATA_SIZE];
-        for (byte i = 0; i < SOURCE_DATA_SIZE; i++) {
-            mSourceData[i] = i;
-        }
-        mSource = new ByteArrayInputStream(mSourceData);
-        mDestination = new ByteArrayOutputStream();
-    }
-
-    @Test
-    public void copyStream_copiesAllBytesIfAsked() throws IOException {
-        StreamUtils.copyStream(mSource, mDestination, mSourceData.length);
-        assertOutputHasBytes(mSourceData.length);
-    }
-
-    @Test
-    public void copyStream_stopsShortIfAsked() throws IOException {
-        StreamUtils.copyStream(mSource, mDestination, mSourceData.length - 10);
-        assertOutputHasBytes(mSourceData.length - 10);
-    }
-
-    @Test
-    public void copyStream_stopsShortIfAskedToCopyMoreThanAvailable() throws IOException {
-        StreamUtils.copyStream(mSource, mDestination, mSourceData.length + 10);
-        assertOutputHasBytes(mSourceData.length);
-    }
-
-    private void assertOutputHasBytes(int count) {
-        byte[] output = mDestination.toByteArray();
-        assertThat(output.length).isEqualTo(count);
-        for (int i = 0; i < count; i++) {
-            assertThat(output[i]).isEqualTo(mSourceData[i]);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
deleted file mode 100644
index c12464c..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunk;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.google.common.primitives.Bytes;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.util.Arrays;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class ChunkHashTest {
-    private static final int HASH_LENGTH_BYTES = 256 / 8;
-    private static final byte[] TEST_HASH_1 = Arrays.copyOf(new byte[] {1}, HASH_LENGTH_BYTES);
-    private static final byte[] TEST_HASH_2 = Arrays.copyOf(new byte[] {2}, HASH_LENGTH_BYTES);
-
-    @Test
-    public void testGetHash_returnsHash() {
-        ChunkHash chunkHash = new ChunkHash(TEST_HASH_1);
-
-        byte[] hash = chunkHash.getHash();
-
-        assertThat(hash).asList().containsExactlyElementsIn(Bytes.asList(TEST_HASH_1)).inOrder();
-    }
-
-    @Test
-    public void testEquals() {
-        ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1);
-        ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1);
-        ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2);
-
-        assertThat(chunkHash1).isEqualTo(equalChunkHash1);
-        assertThat(chunkHash1).isNotEqualTo(chunkHash2);
-    }
-
-    @Test
-    public void testHashCode() {
-        ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1);
-        ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1);
-        ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2);
-
-        int hash1 = chunkHash1.hashCode();
-        int equalHash1 = equalChunkHash1.hashCode();
-        int hash2 = chunkHash2.hashCode();
-
-        assertThat(hash1).isEqualTo(equalHash1);
-        assertThat(hash1).isNotEqualTo(hash2);
-    }
-
-    @Test
-    public void testCompareTo_whenEqual_returnsZero() {
-        ChunkHash chunkHash = new ChunkHash(TEST_HASH_1);
-        ChunkHash equalChunkHash = new ChunkHash(TEST_HASH_1);
-
-        int result = chunkHash.compareTo(equalChunkHash);
-
-        assertThat(result).isEqualTo(0);
-    }
-
-    @Test
-    public void testCompareTo_whenArgumentGreater_returnsNegative() {
-        ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1);
-        ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2);
-
-        int result = chunkHash1.compareTo(chunkHash2);
-
-        assertThat(result).isLessThan(0);
-    }
-
-    @Test
-    public void testCompareTo_whenArgumentSmaller_returnsPositive() {
-        ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1);
-        ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2);
-
-        int result = chunkHash2.compareTo(chunkHash1);
-
-        assertThat(result).isGreaterThan(0);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
deleted file mode 100644
index c5f78c2..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.encryption.chunk;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-
-import com.google.common.base.Charsets;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.util.Arrays;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class ChunkListingMapTest {
-    private static final ChunkHash CHUNK_A_HASH = getHash("CHUNK_A");
-    private static final ChunkHash CHUNK_B_HASH = getHash("CHUNK_B");
-    private static final ChunkHash CHUNK_C_HASH = getHash("CHUNK_C");
-
-    private static final int CHUNK_A_LENGTH = 256;
-    private static final int CHUNK_B_LENGTH = 1024;
-    private static final int CHUNK_C_LENGTH = 4055;
-
-    private static final int CHUNK_A_START = 0;
-    private static final int CHUNK_B_START = CHUNK_A_START + CHUNK_A_LENGTH;
-    private static final int CHUNK_C_START = CHUNK_B_START + CHUNK_B_LENGTH;
-
-    private ChunkListingMap mChunkListingMap;
-
-    @Before
-    public void setUp() {
-        mChunkListingMap = createFromFixture();
-    }
-
-    @Test
-    public void hasChunk_isTrueForExistingChunks() {
-        assertThat(mChunkListingMap.hasChunk(CHUNK_A_HASH)).isTrue();
-        assertThat(mChunkListingMap.hasChunk(CHUNK_B_HASH)).isTrue();
-        assertThat(mChunkListingMap.hasChunk(CHUNK_C_HASH)).isTrue();
-    }
-
-    @Test
-    public void hasChunk_isFalseForNonexistentChunks() {
-        assertThat(mChunkListingMap.hasChunk(getHash("CHUNK_D"))).isFalse();
-        assertThat(mChunkListingMap.hasChunk(getHash(""))).isFalse();
-    }
-
-    @Test
-    public void getChunkListing_hasCorrectLengths() {
-        assertThat(mChunkListingMap.getChunkEntry(CHUNK_A_HASH).getLength())
-                .isEqualTo(CHUNK_A_LENGTH);
-        assertThat(mChunkListingMap.getChunkEntry(CHUNK_B_HASH).getLength())
-                .isEqualTo(CHUNK_B_LENGTH);
-        assertThat(mChunkListingMap.getChunkEntry(CHUNK_C_HASH).getLength())
-                .isEqualTo(CHUNK_C_LENGTH);
-    }
-
-    @Test
-    public void getChunkListing_hasCorrectStarts() {
-        assertThat(mChunkListingMap.getChunkEntry(CHUNK_A_HASH).getStart())
-                .isEqualTo(CHUNK_A_START);
-        assertThat(mChunkListingMap.getChunkEntry(CHUNK_B_HASH).getStart())
-                .isEqualTo(CHUNK_B_START);
-        assertThat(mChunkListingMap.getChunkEntry(CHUNK_C_HASH).getStart())
-                .isEqualTo(CHUNK_C_START);
-    }
-
-    @Test
-    public void getChunkListing_isNullForNonExistentChunks() {
-        assertThat(mChunkListingMap.getChunkEntry(getHash("Hey"))).isNull();
-    }
-
-    private static ChunkListingMap createFromFixture() {
-        ChunksMetadataProto.ChunkListing chunkListing = new ChunksMetadataProto.ChunkListing();
-        chunkListing.chunks = new ChunksMetadataProto.Chunk[3];
-        chunkListing.chunks[0] = newChunk(CHUNK_A_HASH.getHash(), CHUNK_A_LENGTH);
-        chunkListing.chunks[1] = newChunk(CHUNK_B_HASH.getHash(), CHUNK_B_LENGTH);
-        chunkListing.chunks[2] = newChunk(CHUNK_C_HASH.getHash(), CHUNK_C_LENGTH);
-        return ChunkListingMap.fromProto(chunkListing);
-    }
-
-    private static ChunkHash getHash(String name) {
-        return new ChunkHash(
-                Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES));
-    }
-
-    public static ChunksMetadataProto.Chunk newChunk(byte[] hash, int length) {
-        ChunksMetadataProto.Chunk newChunk = new ChunksMetadataProto.Chunk();
-        newChunk.hash = Arrays.copyOf(hash, hash.length);
-        newChunk.length = length;
-        return newChunk;
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
deleted file mode 100644
index c6b29b7b..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunk;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.google.common.primitives.Bytes;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class EncryptedChunkOrderingTest {
-    private static final byte[] TEST_BYTE_ARRAY_1 = new byte[] {1, 2, 3, 4, 5};
-    private static final byte[] TEST_BYTE_ARRAY_2 = new byte[] {5, 4, 3, 2, 1};
-
-    @Test
-    public void testEncryptedChunkOrdering_returnsValue() {
-        EncryptedChunkOrdering encryptedChunkOrdering =
-                EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1);
-
-        byte[] bytes = encryptedChunkOrdering.encryptedChunkOrdering();
-
-        assertThat(bytes)
-                .asList()
-                .containsExactlyElementsIn(Bytes.asList(TEST_BYTE_ARRAY_1))
-                .inOrder();
-    }
-
-    @Test
-    public void testEquals() {
-        EncryptedChunkOrdering chunkOrdering1 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1);
-        EncryptedChunkOrdering equalChunkOrdering1 =
-                EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1);
-        EncryptedChunkOrdering chunkOrdering2 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_2);
-
-        assertThat(chunkOrdering1).isEqualTo(equalChunkOrdering1);
-        assertThat(chunkOrdering1).isNotEqualTo(chunkOrdering2);
-    }
-
-    @Test
-    public void testHashCode() {
-        EncryptedChunkOrdering chunkOrdering1 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1);
-        EncryptedChunkOrdering equalChunkOrdering1 =
-                EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1);
-        EncryptedChunkOrdering chunkOrdering2 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_2);
-
-        int hash1 = chunkOrdering1.hashCode();
-        int equalHash1 = equalChunkOrdering1.hashCode();
-        int hash2 = chunkOrdering2.hashCode();
-
-        assertThat(hash1).isEqualTo(equalHash1);
-        assertThat(hash1).isNotEqualTo(hash2);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/BackupFileBuilderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/BackupFileBuilderTest.java
deleted file mode 100644
index 590938e..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/BackupFileBuilderTest.java
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- * 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.encryption.chunking;
-
-import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.AES_256_GCM;
-import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
-import static com.android.server.backup.testing.CryptoTestUtils.newChunk;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static junit.framework.Assert.fail;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.testng.Assert.assertThrows;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.testing.DiffScriptProcessor;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.Files;
-import com.google.common.primitives.Bytes;
-import com.google.common.primitives.Longs;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class BackupFileBuilderTest {
-    private static final String TEST_DATA_1 =
-            "I'm already there or close to [T7-9/executive level] in terms of big-picture vision";
-    private static final String TEST_DATA_2 =
-            "I was known for Real Games and should have been brought in for advice";
-    private static final String TEST_DATA_3 =
-            "Pride is rooted in the delusional belief held by all humans in an unchanging self";
-
-    private static final byte[] TEST_FINGERPRINT_MIXER_SALT =
-            Arrays.copyOf(new byte[] {22}, ChunkHash.HASH_LENGTH_BYTES);
-
-    private static final ChunkHash TEST_HASH_1 =
-            new ChunkHash(Arrays.copyOf(new byte[] {0}, EncryptedChunk.KEY_LENGTH_BYTES));
-    private static final ChunkHash TEST_HASH_2 =
-            new ChunkHash(Arrays.copyOf(new byte[] {1}, EncryptedChunk.KEY_LENGTH_BYTES));
-    private static final ChunkHash TEST_HASH_3 =
-            new ChunkHash(Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES));
-
-    private static final byte[] TEST_NONCE =
-            Arrays.copyOf(new byte[] {3}, EncryptedChunk.NONCE_LENGTH_BYTES);
-
-    private static final EncryptedChunk TEST_CHUNK_1 =
-            EncryptedChunk.create(TEST_HASH_1, TEST_NONCE, TEST_DATA_1.getBytes(UTF_8));
-    private static final EncryptedChunk TEST_CHUNK_2 =
-            EncryptedChunk.create(TEST_HASH_2, TEST_NONCE, TEST_DATA_2.getBytes(UTF_8));
-    private static final EncryptedChunk TEST_CHUNK_3 =
-            EncryptedChunk.create(TEST_HASH_3, TEST_NONCE, TEST_DATA_3.getBytes(UTF_8));
-
-    private static final byte[] TEST_CHECKSUM = {1, 2, 3, 4, 5, 6};
-
-    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
-    private File mOldFile;
-    private ChunksMetadataProto.ChunkListing mOldChunkListing;
-    private EncryptedChunkEncoder mEncryptedChunkEncoder;
-
-    @Before
-    public void setUp() {
-        mEncryptedChunkEncoder = new LengthlessEncryptedChunkEncoder();
-    }
-
-    @Test
-    public void writeChunks_nonIncremental_writesCorrectRawData() throws Exception {
-        ByteArrayOutputStream output = new ByteArrayOutputStream();
-        BackupFileBuilder backupFileBuilder = BackupFileBuilder.createForNonIncremental(output);
-
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_1, TEST_HASH_2),
-                getNewChunkMap(TEST_HASH_1, TEST_HASH_2));
-
-        byte[] actual = output.toByteArray();
-        byte[] expected =
-                Bytes.concat(
-                        TEST_CHUNK_1.nonce(),
-                        TEST_CHUNK_1.encryptedBytes(),
-                        TEST_CHUNK_2.nonce(),
-                        TEST_CHUNK_2.encryptedBytes());
-        assertThat(actual).asList().containsExactlyElementsIn(Bytes.asList(expected)).inOrder();
-    }
-
-    @Test
-    public void writeChunks_nonIncrementalWithDuplicates_writesEachChunkOnlyOnce()
-            throws Exception {
-        ByteArrayOutputStream output = new ByteArrayOutputStream();
-        BackupFileBuilder backupFileBuilder = BackupFileBuilder.createForNonIncremental(output);
-
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_1),
-                getNewChunkMap(TEST_HASH_1, TEST_HASH_2));
-
-        byte[] actual = output.toByteArray();
-        byte[] expected =
-                Bytes.concat(
-                        TEST_CHUNK_1.nonce(),
-                        TEST_CHUNK_1.encryptedBytes(),
-                        TEST_CHUNK_2.nonce(),
-                        TEST_CHUNK_2.encryptedBytes());
-        assertThat(actual).asList().containsExactlyElementsIn(Bytes.asList(expected)).inOrder();
-    }
-
-    @Test
-    public void writeChunks_incremental_writesParsableDiffScript() throws Exception {
-        // We will insert chunk 2 in between chunks 1 and 3.
-        setUpOldBackupWithChunks(ImmutableList.of(TEST_CHUNK_1, TEST_CHUNK_3));
-        ByteArrayOutputStream diffOutputStream = new ByteArrayOutputStream();
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(diffOutputStream, mOldChunkListing);
-
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_3),
-                getNewChunkMap(TEST_HASH_2));
-        backupFileBuilder.finish(getTestMetadata());
-
-        byte[] actual =
-                stripMetadataAndPositionFromOutput(parseDiffScript(diffOutputStream.toByteArray()));
-        byte[] expected =
-                Bytes.concat(
-                        TEST_CHUNK_1.nonce(),
-                        TEST_CHUNK_1.encryptedBytes(),
-                        TEST_CHUNK_2.nonce(),
-                        TEST_CHUNK_2.encryptedBytes(),
-                        TEST_CHUNK_3.nonce(),
-                        TEST_CHUNK_3.encryptedBytes());
-        assertThat(actual).asList().containsExactlyElementsIn(Bytes.asList(expected)).inOrder();
-    }
-
-    @Test
-    public void writeChunks_incrementalWithDuplicates_writesEachChunkOnlyOnce() throws Exception {
-        // We will insert chunk 2 twice in between chunks 1 and 3.
-        setUpOldBackupWithChunks(ImmutableList.of(TEST_CHUNK_1, TEST_CHUNK_3));
-        ByteArrayOutputStream diffOutputStream = new ByteArrayOutputStream();
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(diffOutputStream, mOldChunkListing);
-
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_2, TEST_HASH_3),
-                getNewChunkMap(TEST_HASH_2));
-        backupFileBuilder.finish(getTestMetadata());
-
-        byte[] actual =
-                stripMetadataAndPositionFromOutput(parseDiffScript(diffOutputStream.toByteArray()));
-        byte[] expected =
-                Bytes.concat(
-                        TEST_CHUNK_1.nonce(),
-                        TEST_CHUNK_1.encryptedBytes(),
-                        TEST_CHUNK_2.nonce(),
-                        TEST_CHUNK_2.encryptedBytes(),
-                        TEST_CHUNK_3.nonce(),
-                        TEST_CHUNK_3.encryptedBytes());
-        assertThat(actual).asList().containsExactlyElementsIn(Bytes.asList(expected)).inOrder();
-    }
-
-    @Test
-    public void writeChunks_writesChunksInOrderOfHash() throws Exception {
-        setUpOldBackupWithChunks(ImmutableList.of());
-        ByteArrayOutputStream diffOutputStream = new ByteArrayOutputStream();
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(diffOutputStream, mOldChunkListing);
-
-        // Write chunks out of order.
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_2, TEST_HASH_1),
-                getNewChunkMap(TEST_HASH_2, TEST_HASH_1));
-        backupFileBuilder.finish(getTestMetadata());
-
-        byte[] actual =
-                stripMetadataAndPositionFromOutput(parseDiffScript(diffOutputStream.toByteArray()));
-        byte[] expected =
-                Bytes.concat(
-                        TEST_CHUNK_1.nonce(),
-                        TEST_CHUNK_1.encryptedBytes(),
-                        TEST_CHUNK_2.nonce(),
-                        TEST_CHUNK_2.encryptedBytes());
-        assertThat(actual).asList().containsExactlyElementsIn(Bytes.asList(expected)).inOrder();
-    }
-
-    @Test
-    public void writeChunks_alreadyFlushed_throwsException() throws Exception {
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), new ChunksMetadataProto.ChunkListing());
-        backupFileBuilder.finish(getTestMetadata());
-
-        assertThrows(
-                IllegalStateException.class,
-                () -> backupFileBuilder.writeChunks(ImmutableList.of(), getNewChunkMap()));
-    }
-
-    @Test
-    public void getNewChunkListing_hasChunksInOrderOfKey() throws Exception {
-        // We will insert chunk 2 in between chunks 1 and 3.
-        setUpOldBackupWithChunks(ImmutableList.of(TEST_CHUNK_1, TEST_CHUNK_3));
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), mOldChunkListing);
-
-        // Write chunks out of order.
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_1, TEST_HASH_3, TEST_HASH_2),
-                getNewChunkMap(TEST_HASH_2));
-        backupFileBuilder.finish(getTestMetadata());
-
-        ChunksMetadataProto.ChunkListing expected = expectedChunkListing();
-        ChunksMetadataProto.ChunkListing actual =
-                backupFileBuilder.getNewChunkListing(TEST_FINGERPRINT_MIXER_SALT);
-        assertListingsEqual(actual, expected);
-    }
-
-    @Test
-    public void getNewChunkListing_writeChunksInTwoBatches_returnsListingContainingAllChunks()
-            throws Exception {
-        // We will insert chunk 2 in between chunks 1 and 3.
-        setUpOldBackupWithChunks(ImmutableList.of(TEST_CHUNK_1, TEST_CHUNK_3));
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), mOldChunkListing);
-
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_1, TEST_HASH_2), getNewChunkMap(TEST_HASH_2));
-        backupFileBuilder.writeChunks(ImmutableList.of(TEST_HASH_3), getNewChunkMap(TEST_HASH_2));
-        backupFileBuilder.finish(getTestMetadata());
-
-        ChunksMetadataProto.ChunkListing expected = expectedChunkListing();
-        ChunksMetadataProto.ChunkListing actual =
-                backupFileBuilder.getNewChunkListing(TEST_FINGERPRINT_MIXER_SALT);
-        assertListingsEqual(actual, expected);
-    }
-
-    @Test
-    public void getNewChunkListing_writeDuplicateChunks_writesEachChunkOnlyOnce() throws Exception {
-        // We will append [2][3][3][2] onto [1].
-        setUpOldBackupWithChunks(ImmutableList.of(TEST_CHUNK_1));
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), mOldChunkListing);
-
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_3),
-                getNewChunkMap(TEST_HASH_3, TEST_HASH_2));
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_3, TEST_HASH_2),
-                getNewChunkMap(TEST_HASH_3, TEST_HASH_2));
-        backupFileBuilder.finish(getTestMetadata());
-
-        ChunksMetadataProto.ChunkListing expected = expectedChunkListing();
-        ChunksMetadataProto.ChunkListing actual =
-                backupFileBuilder.getNewChunkListing(TEST_FINGERPRINT_MIXER_SALT);
-        assertListingsEqual(actual, expected);
-    }
-
-    @Test
-    public void getNewChunkListing_nonIncrementalWithNoSalt_doesNotThrowOnSerialisation() {
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForNonIncremental(new ByteArrayOutputStream());
-
-        ChunksMetadataProto.ChunkListing newChunkListing =
-                backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
-
-        // Does not throw.
-        ChunksMetadataProto.ChunkListing.toByteArray(newChunkListing);
-    }
-
-    @Test
-    public void getNewChunkListing_incrementalWithNoSalt_doesNotThrowOnSerialisation()
-            throws Exception {
-
-        setUpOldBackupWithChunks(ImmutableList.of());
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), mOldChunkListing);
-
-        ChunksMetadataProto.ChunkListing newChunkListing =
-                backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
-
-        // Does not throw.
-        ChunksMetadataProto.ChunkListing.toByteArray(newChunkListing);
-    }
-
-    @Test
-    public void getNewChunkListing_nonIncrementalWithNoSalt_hasEmptySalt() {
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForNonIncremental(new ByteArrayOutputStream());
-
-        ChunksMetadataProto.ChunkListing newChunkListing =
-                backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
-
-        assertThat(newChunkListing.fingerprintMixerSalt).isEmpty();
-    }
-
-    @Test
-    public void getNewChunkListing_incrementalWithNoSalt_hasEmptySalt() throws Exception {
-        setUpOldBackupWithChunks(ImmutableList.of());
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), mOldChunkListing);
-
-        ChunksMetadataProto.ChunkListing newChunkListing =
-                backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
-
-        assertThat(newChunkListing.fingerprintMixerSalt).isEmpty();
-    }
-
-    @Test
-    public void getNewChunkListing_nonIncrementalWithSalt_hasGivenSalt() {
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForNonIncremental(new ByteArrayOutputStream());
-
-        ChunksMetadataProto.ChunkListing newChunkListing =
-                backupFileBuilder.getNewChunkListing(TEST_FINGERPRINT_MIXER_SALT);
-
-        assertThat(newChunkListing.fingerprintMixerSalt).isEqualTo(TEST_FINGERPRINT_MIXER_SALT);
-    }
-
-    @Test
-    public void getNewChunkListing_incrementalWithSalt_hasGivenSalt() throws Exception {
-        setUpOldBackupWithChunks(ImmutableList.of());
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), mOldChunkListing);
-
-        ChunksMetadataProto.ChunkListing newChunkListing =
-                backupFileBuilder.getNewChunkListing(TEST_FINGERPRINT_MIXER_SALT);
-
-        assertThat(newChunkListing.fingerprintMixerSalt).isEqualTo(TEST_FINGERPRINT_MIXER_SALT);
-    }
-
-    @Test
-    public void getNewChunkListing_nonIncremental_hasCorrectCipherTypeAndChunkOrderingType() {
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForNonIncremental(new ByteArrayOutputStream());
-
-        ChunksMetadataProto.ChunkListing newChunkListing =
-                backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
-
-        assertThat(newChunkListing.cipherType).isEqualTo(ChunksMetadataProto.AES_256_GCM);
-        assertThat(newChunkListing.chunkOrderingType)
-                .isEqualTo(ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED);
-    }
-
-    @Test
-    public void getNewChunkListing_incremental_hasCorrectCipherTypeAndChunkOrderingType()
-            throws Exception {
-        setUpOldBackupWithChunks(ImmutableList.of());
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), mOldChunkListing);
-
-        ChunksMetadataProto.ChunkListing newChunkListing =
-                backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
-
-        assertThat(newChunkListing.cipherType).isEqualTo(ChunksMetadataProto.AES_256_GCM);
-        assertThat(newChunkListing.chunkOrderingType)
-                .isEqualTo(ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED);
-    }
-
-    @Test
-    public void getNewChunkOrdering_chunksHaveCorrectStartPositions() throws Exception {
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), new ChunksMetadataProto.ChunkListing());
-
-        // Write out of order by key to check that ordering is maintained.
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_1, TEST_HASH_3, TEST_HASH_2),
-                getNewChunkMap(TEST_HASH_1, TEST_HASH_3, TEST_HASH_2));
-        backupFileBuilder.finish(getTestMetadata());
-
-        ChunksMetadataProto.ChunkOrdering actual =
-                backupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM);
-        // The chunks are listed in the order they are written above, but the start positions are
-        // determined by the order in the encrypted blob (which is lexicographical by key).
-        int chunk1Start = 0;
-        int chunk2Start =
-                chunk1Start + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_1);
-        int chunk3Start =
-                chunk2Start + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_2);
-
-        int[] expected = {chunk1Start, chunk3Start, chunk2Start};
-        assertThat(actual.starts.length).isEqualTo(expected.length);
-        for (int i = 0; i < actual.starts.length; i++) {
-            assertThat(expected[i]).isEqualTo(actual.starts[i]);
-        }
-    }
-
-    @Test
-    public void getNewChunkOrdering_duplicateChunks_writesDuplicates() throws Exception {
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), new ChunksMetadataProto.ChunkListing());
-
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_2),
-                getNewChunkMap(TEST_HASH_1, TEST_HASH_2));
-        backupFileBuilder.writeChunks(
-                ImmutableList.of(TEST_HASH_3, TEST_HASH_3), getNewChunkMap(TEST_HASH_3));
-        backupFileBuilder.finish(getTestMetadata());
-
-        ChunksMetadataProto.ChunkOrdering actual =
-                backupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM);
-        int chunk1Start = 0;
-        int chunk2Start =
-                chunk1Start + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_1);
-        int chunk3Start =
-                chunk2Start + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_2);
-
-        int[] expected = {chunk1Start, chunk2Start, chunk2Start, chunk3Start, chunk3Start};
-        assertThat(actual.starts.length).isEqualTo(expected.length);
-        for (int i = 0; i < actual.starts.length; i++) {
-            assertThat(expected[i]).isEqualTo(actual.starts[i]);
-        }
-    }
-
-    @Test
-    public void getNewChunkOrdering_returnsOrderingWithChecksum() throws Exception {
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        new ByteArrayOutputStream(), new ChunksMetadataProto.ChunkListing());
-
-        backupFileBuilder.writeChunks(ImmutableList.of(TEST_HASH_1), getNewChunkMap(TEST_HASH_1));
-        backupFileBuilder.finish(getTestMetadata());
-
-        ChunksMetadataProto.ChunkOrdering actual =
-                backupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM);
-        assertThat(actual.checksum).isEqualTo(TEST_CHECKSUM);
-    }
-
-    @Test
-    public void finish_writesMetadata() throws Exception {
-        ByteArrayOutputStream output = new ByteArrayOutputStream();
-        BackupFileBuilder builder = BackupFileBuilder.createForNonIncremental(output);
-        ChunksMetadataProto.ChunksMetadata expectedMetadata = getTestMetadata();
-
-        builder.finish(expectedMetadata);
-
-        // The output is [metadata]+[long giving size of metadata].
-        byte[] metadataBytes =
-                Arrays.copyOfRange(output.toByteArray(), 0, output.size() - Long.BYTES);
-        ChunksMetadataProto.ChunksMetadata actualMetadata =
-                ChunksMetadataProto.ChunksMetadata.parseFrom(metadataBytes);
-        assertThat(actualMetadata.checksumType).isEqualTo(ChunksMetadataProto.SHA_256);
-        assertThat(actualMetadata.cipherType).isEqualTo(ChunksMetadataProto.AES_256_GCM);
-    }
-
-    @Test
-    public void finish_writesMetadataPosition() throws Exception {
-        ByteArrayOutputStream output = new ByteArrayOutputStream();
-        BackupFileBuilder builder = BackupFileBuilder.createForNonIncremental(output);
-
-        builder.writeChunks(
-                ImmutableList.of(TEST_HASH_1, TEST_HASH_2),
-                getNewChunkMap(TEST_HASH_1, TEST_HASH_2));
-        builder.writeChunks(ImmutableList.of(TEST_HASH_3), getNewChunkMap(TEST_HASH_3));
-        builder.finish(getTestMetadata());
-
-        long expectedPosition =
-                (long) mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_1)
-                        + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_2)
-                        + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_3);
-        long actualPosition =
-                Longs.fromByteArray(
-                        Arrays.copyOfRange(
-                                output.toByteArray(), output.size() - Long.BYTES, output.size()));
-        assertThat(actualPosition).isEqualTo(expectedPosition);
-    }
-
-    @Test
-    public void finish_flushesOutputStream() throws Exception {
-        OutputStream diffOutputStream = mock(OutputStream.class);
-        BackupFileBuilder backupFileBuilder =
-                BackupFileBuilder.createForIncremental(
-                        diffOutputStream, new ChunksMetadataProto.ChunkListing());
-
-        backupFileBuilder.writeChunks(ImmutableList.of(TEST_HASH_1), getNewChunkMap(TEST_HASH_1));
-        diffOutputStream.flush();
-
-        verify(diffOutputStream).flush();
-    }
-
-    private void setUpOldBackupWithChunks(List<EncryptedChunk> chunks) throws Exception {
-        mOldFile = mTemporaryFolder.newFile();
-        ChunksMetadataProto.ChunkListing chunkListing = new ChunksMetadataProto.ChunkListing();
-        chunkListing.fingerprintMixerSalt =
-                Arrays.copyOf(TEST_FINGERPRINT_MIXER_SALT, TEST_FINGERPRINT_MIXER_SALT.length);
-        chunkListing.cipherType = AES_256_GCM;
-        chunkListing.chunkOrderingType = CHUNK_ORDERING_TYPE_UNSPECIFIED;
-
-        List<ChunksMetadataProto.Chunk> knownChunks = new ArrayList<>();
-        try (FileOutputStream outputStream = new FileOutputStream(mOldFile)) {
-            for (EncryptedChunk chunk : chunks) {
-                // Chunks are encoded in the format [nonce]+[data].
-                outputStream.write(chunk.nonce());
-                outputStream.write(chunk.encryptedBytes());
-
-                knownChunks.add(createChunkFor(chunk));
-            }
-
-            outputStream.flush();
-        }
-
-        chunkListing.chunks = knownChunks.toArray(new ChunksMetadataProto.Chunk[0]);
-        mOldChunkListing = chunkListing;
-    }
-
-    private byte[] parseDiffScript(byte[] diffScript) throws Exception {
-        File newFile = mTemporaryFolder.newFile();
-        new DiffScriptProcessor(mOldFile, newFile).process(new ByteArrayInputStream(diffScript));
-        return Files.toByteArray(newFile);
-    }
-
-    private void assertListingsEqual(
-            ChunksMetadataProto.ChunkListing result, ChunksMetadataProto.ChunkListing expected) {
-        assertThat(result.chunks.length).isEqualTo(expected.chunks.length);
-        for (int i = 0; i < result.chunks.length; i++) {
-            assertWithMessage("Chunk " + i)
-                    .that(result.chunks[i].length)
-                    .isEqualTo(expected.chunks[i].length);
-            assertWithMessage("Chunk " + i)
-                    .that(result.chunks[i].hash)
-                    .isEqualTo(expected.chunks[i].hash);
-        }
-    }
-
-    private static ImmutableMap<ChunkHash, EncryptedChunk> getNewChunkMap(ChunkHash... hashes) {
-        ImmutableMap.Builder<ChunkHash, EncryptedChunk> builder = ImmutableMap.builder();
-        for (ChunkHash hash : hashes) {
-            if (TEST_HASH_1.equals(hash)) {
-                builder.put(TEST_HASH_1, TEST_CHUNK_1);
-            } else if (TEST_HASH_2.equals(hash)) {
-                builder.put(TEST_HASH_2, TEST_CHUNK_2);
-            } else if (TEST_HASH_3.equals(hash)) {
-                builder.put(TEST_HASH_3, TEST_CHUNK_3);
-            } else {
-                fail("Hash was not recognised: " + hash);
-            }
-        }
-        return builder.build();
-    }
-
-    private static ChunksMetadataProto.ChunksMetadata getTestMetadata() {
-        ChunksMetadataProto.ChunksMetadata metadata = new ChunksMetadataProto.ChunksMetadata();
-        metadata.checksumType = ChunksMetadataProto.SHA_256;
-        metadata.cipherType = AES_256_GCM;
-        return metadata;
-    }
-
-    private static byte[] stripMetadataAndPositionFromOutput(byte[] output) {
-        long metadataStart =
-                Longs.fromByteArray(
-                        Arrays.copyOfRange(output, output.length - Long.BYTES, output.length));
-        return Arrays.copyOfRange(output, 0, (int) metadataStart);
-    }
-
-    private ChunksMetadataProto.ChunkListing expectedChunkListing() {
-        ChunksMetadataProto.ChunkListing chunkListing = new ChunksMetadataProto.ChunkListing();
-        chunkListing.fingerprintMixerSalt =
-                Arrays.copyOf(TEST_FINGERPRINT_MIXER_SALT, TEST_FINGERPRINT_MIXER_SALT.length);
-        chunkListing.cipherType = AES_256_GCM;
-        chunkListing.chunkOrderingType = CHUNK_ORDERING_TYPE_UNSPECIFIED;
-        chunkListing.chunks = new ChunksMetadataProto.Chunk[3];
-        chunkListing.chunks[0] = createChunkFor(TEST_CHUNK_1);
-        chunkListing.chunks[1] = createChunkFor(TEST_CHUNK_2);
-        chunkListing.chunks[2] = createChunkFor(TEST_CHUNK_3);
-        return chunkListing;
-    }
-
-    private ChunksMetadataProto.Chunk createChunkFor(EncryptedChunk encryptedChunk) {
-        byte[] chunkHash = encryptedChunk.key().getHash();
-        byte[] hashCopy = Arrays.copyOf(chunkHash, chunkHash.length);
-        return newChunk(hashCopy, mEncryptedChunkEncoder.getEncodedLengthOfChunk(encryptedChunk));
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
deleted file mode 100644
index 8df0826..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import static org.junit.Assert.assertEquals;
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-/** Tests for {@link ByteRange}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class ByteRangeTest {
-    @Test
-    public void getLength_includesEnd() throws Exception {
-        ByteRange byteRange = new ByteRange(5, 10);
-
-        int length = byteRange.getLength();
-
-        assertEquals(6, length);
-    }
-
-    @Test
-    public void constructor_rejectsNegativeStart() {
-        assertThrows(IllegalArgumentException.class, () -> new ByteRange(-1, 10));
-    }
-
-    @Test
-    public void constructor_rejectsEndBeforeStart() {
-        assertThrows(IllegalArgumentException.class, () -> new ByteRange(10, 9));
-    }
-
-    @Test
-    public void extend_withZeroLength_throwsException() {
-        ByteRange byteRange = new ByteRange(5, 10);
-
-        assertThrows(IllegalArgumentException.class, () -> byteRange.extend(0));
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
deleted file mode 100644
index 19e3b28..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.robolectric.RobolectricTestRunner;
-
-import java.security.SecureRandom;
-
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class ChunkEncryptorTest {
-    private static final String MAC_ALGORITHM = "HmacSHA256";
-    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
-    private static final int GCM_NONCE_LENGTH_BYTES = 12;
-    private static final int GCM_TAG_LENGTH_BYTES = 16;
-    private static final String CHUNK_PLAINTEXT =
-            "A little Learning is a dang'rous Thing;\n"
-                    + "Drink deep, or taste not the Pierian Spring:\n"
-                    + "There shallow Draughts intoxicate the Brain,\n"
-                    + "And drinking largely sobers us again.";
-    private static final byte[] PLAINTEXT_BYTES = CHUNK_PLAINTEXT.getBytes(UTF_8);
-    private static final byte[] NONCE_1 = "0123456789abc".getBytes(UTF_8);
-    private static final byte[] NONCE_2 = "123456789abcd".getBytes(UTF_8);
-
-    private static final byte[][] NONCES = new byte[][] {NONCE_1, NONCE_2};
-
-    @Mock private SecureRandom mSecureRandomMock;
-    private SecretKey mSecretKey;
-    private ChunkHash mPlaintextHash;
-    private ChunkEncryptor mChunkEncryptor;
-
-    @Before
-    public void setUp() throws Exception {
-        mSecretKey = generateAesKey();
-        ChunkHasher chunkHasher = new ChunkHasher(mSecretKey);
-        mPlaintextHash = chunkHasher.computeHash(PLAINTEXT_BYTES);
-        mSecureRandomMock = mock(SecureRandom.class);
-        mChunkEncryptor = new ChunkEncryptor(mSecretKey, mSecureRandomMock);
-
-        // Return NONCE_1, then NONCE_2 for invocations of mSecureRandomMock.nextBytes().
-        doAnswer(
-                    new Answer<Void>() {
-                        private int mInvocation = 0;
-
-                        @Override
-                        public Void answer(InvocationOnMock invocation) {
-                            byte[] nonceDestination = invocation.getArgument(0);
-                            System.arraycopy(
-                                    NONCES[this.mInvocation],
-                                    0,
-                                    nonceDestination,
-                                    0,
-                                    GCM_NONCE_LENGTH_BYTES);
-                            this.mInvocation++;
-                            return null;
-                        }
-                    })
-                .when(mSecureRandomMock)
-                .nextBytes(any(byte[].class));
-    }
-
-    @Test
-    public void encrypt_withHash_resultContainsHashAsKey() throws Exception {
-        EncryptedChunk chunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
-
-        assertThat(chunk.key()).isEqualTo(mPlaintextHash);
-    }
-
-    @Test
-    public void encrypt_generatesHmacOfPlaintext() throws Exception {
-        EncryptedChunk chunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
-
-        byte[] generatedHash = chunk.key().getHash();
-        Mac mac = Mac.getInstance(MAC_ALGORITHM);
-        mac.init(mSecretKey);
-        byte[] plaintextHmac = mac.doFinal(PLAINTEXT_BYTES);
-        assertThat(generatedHash).isEqualTo(plaintextHmac);
-    }
-
-    @Test
-    public void encrypt_whenInvokedAgain_generatesNewNonce() throws Exception {
-        EncryptedChunk chunk1 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
-
-        EncryptedChunk chunk2 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
-
-        assertThat(chunk1.nonce()).isNotEqualTo(chunk2.nonce());
-    }
-
-    @Test
-    public void encrypt_whenInvokedAgain_generatesNewCiphertext() throws Exception {
-        EncryptedChunk chunk1 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
-
-        EncryptedChunk chunk2 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
-
-        assertThat(chunk1.encryptedBytes()).isNotEqualTo(chunk2.encryptedBytes());
-    }
-
-    @Test
-    public void encrypt_generates12ByteNonce() throws Exception {
-        EncryptedChunk encryptedChunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
-
-        byte[] nonce = encryptedChunk.nonce();
-        assertThat(nonce).hasLength(GCM_NONCE_LENGTH_BYTES);
-    }
-
-    @Test
-    public void encrypt_decryptedResultCorrespondsToPlaintext() throws Exception {
-        EncryptedChunk chunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
-
-        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
-        cipher.init(
-                Cipher.DECRYPT_MODE,
-                mSecretKey,
-                new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * 8, chunk.nonce()));
-        byte[] decrypted = cipher.doFinal(chunk.encryptedBytes());
-        assertThat(decrypted).isEqualTo(PLAINTEXT_BYTES);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
deleted file mode 100644
index 72a927d..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import javax.crypto.Mac;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class ChunkHasherTest {
-    private static final String KEY_ALGORITHM = "AES";
-    private static final String MAC_ALGORITHM = "HmacSHA256";
-
-    private static final byte[] TEST_KEY = {100, 120};
-    private static final byte[] TEST_DATA = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
-
-    private SecretKey mSecretKey;
-    private ChunkHasher mChunkHasher;
-
-    @Before
-    public void setUp() throws Exception {
-        mSecretKey = new SecretKeySpec(TEST_KEY, KEY_ALGORITHM);
-        mChunkHasher = new ChunkHasher(mSecretKey);
-    }
-
-    @Test
-    public void computeHash_returnsHmacForData() throws Exception {
-        ChunkHash chunkHash = mChunkHasher.computeHash(TEST_DATA);
-
-        byte[] hash = chunkHash.getHash();
-        Mac mac = Mac.getInstance(MAC_ALGORITHM);
-        mac.init(mSecretKey);
-        byte[] expectedHash = mac.doFinal(TEST_DATA);
-        assertThat(hash).isEqualTo(expectedHash);
-    }
-
-    @Test
-    public void computeHash_generates256BitHmac() throws Exception {
-        int expectedLength = 256 / Byte.SIZE;
-
-        byte[] hash = mChunkHasher.computeHash(TEST_DATA).getHash();
-
-        assertThat(hash).hasLength(expectedLength);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutputTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutputTest.java
deleted file mode 100644
index 823a63c..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutputTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.encryption.chunking;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.tasks.DecryptedChunkOutput;
-
-import com.google.common.io.Files;
-import com.google.common.primitives.Bytes;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.util.Arrays;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class DecryptedChunkFileOutputTest {
-    private static final byte[] TEST_CHUNK_1 = {1, 2, 3};
-    private static final byte[] TEST_CHUNK_2 = {4, 5, 6, 7, 8, 9, 10};
-    private static final int TEST_BUFFER_LENGTH =
-            Math.max(TEST_CHUNK_1.length, TEST_CHUNK_2.length);
-
-    @Rule
-    public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
-    private File mOutputFile;
-    private DecryptedChunkFileOutput mDecryptedChunkFileOutput;
-
-    @Before
-    public void setUp() throws Exception {
-        mOutputFile = temporaryFolder.newFile();
-        mDecryptedChunkFileOutput = new DecryptedChunkFileOutput(mOutputFile);
-    }
-
-    @Test
-    public void open_returnsInstance() throws Exception {
-        DecryptedChunkOutput result = mDecryptedChunkFileOutput.open();
-        assertThat(result).isEqualTo(mDecryptedChunkFileOutput);
-    }
-
-    @Test
-    public void open_nonExistentOutputFolder_throwsException() throws Exception {
-        mDecryptedChunkFileOutput =
-                new DecryptedChunkFileOutput(
-                        new File(temporaryFolder.newFolder(), "mOutput/directory"));
-        assertThrows(FileNotFoundException.class, () -> mDecryptedChunkFileOutput.open());
-    }
-
-    @Test
-    public void open_whenRunTwice_throwsException() throws Exception {
-        mDecryptedChunkFileOutput.open();
-        assertThrows(IllegalStateException.class, () -> mDecryptedChunkFileOutput.open());
-    }
-
-    @Test
-    public void processChunk_beforeOpen_throwsException() throws Exception {
-        assertThrows(IllegalStateException.class,
-                () -> mDecryptedChunkFileOutput.processChunk(new byte[0], 0));
-    }
-
-    @Test
-    public void processChunk_writesChunksToFile() throws Exception {
-        processTestChunks();
-
-        assertThat(Files.toByteArray(mOutputFile))
-                .isEqualTo(Bytes.concat(TEST_CHUNK_1, TEST_CHUNK_2));
-    }
-
-    @Test
-    public void getDigest_beforeClose_throws() throws Exception {
-        mDecryptedChunkFileOutput.open();
-        assertThrows(IllegalStateException.class, () -> mDecryptedChunkFileOutput.getDigest());
-    }
-
-    @Test
-    public void getDigest_returnsCorrectDigest() throws Exception {
-        processTestChunks();
-
-        byte[] actualDigest = mDecryptedChunkFileOutput.getDigest();
-
-        MessageDigest expectedDigest =
-                MessageDigest.getInstance(DecryptedChunkFileOutput.DIGEST_ALGORITHM);
-        expectedDigest.update(TEST_CHUNK_1);
-        expectedDigest.update(TEST_CHUNK_2);
-        assertThat(actualDigest).isEqualTo(expectedDigest.digest());
-    }
-
-    @Test
-    public void getDigest_whenRunTwice_returnsIdenticalDigestBothTimes() throws Exception {
-        processTestChunks();
-
-        byte[] digest1 = mDecryptedChunkFileOutput.getDigest();
-        byte[] digest2 = mDecryptedChunkFileOutput.getDigest();
-
-        assertThat(digest1).isEqualTo(digest2);
-    }
-
-    private void processTestChunks() throws IOException {
-        mDecryptedChunkFileOutput.open();
-        mDecryptedChunkFileOutput.processChunk(Arrays.copyOf(TEST_CHUNK_1, TEST_BUFFER_LENGTH),
-                TEST_CHUNK_1.length);
-        mDecryptedChunkFileOutput.processChunk(Arrays.copyOf(TEST_CHUNK_2, TEST_BUFFER_LENGTH),
-                TEST_CHUNK_2.length);
-        mDecryptedChunkFileOutput.close();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
deleted file mode 100644
index 2af6f2b..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.google.common.primitives.Bytes;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.IOException;
-
-/** Tests for {@link DiffScriptBackupWriter}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class DiffScriptBackupWriterTest {
-    private static final byte[] TEST_BYTES = {1, 2, 3, 4, 5, 6, 7, 8, 9};
-
-    @Captor private ArgumentCaptor<Byte> mBytesCaptor;
-    @Mock private SingleStreamDiffScriptWriter mDiffScriptWriter;
-    private BackupWriter mBackupWriter;
-
-    @Before
-    public void setUp() {
-        mDiffScriptWriter = mock(SingleStreamDiffScriptWriter.class);
-        mBackupWriter = new DiffScriptBackupWriter(mDiffScriptWriter);
-        mBytesCaptor = ArgumentCaptor.forClass(Byte.class);
-    }
-
-    @Test
-    public void writeBytes_writesBytesToWriter() throws Exception {
-        mBackupWriter.writeBytes(TEST_BYTES);
-
-        verify(mDiffScriptWriter, atLeastOnce()).writeByte(mBytesCaptor.capture());
-        assertThat(mBytesCaptor.getAllValues())
-                .containsExactlyElementsIn(Bytes.asList(TEST_BYTES))
-                .inOrder();
-    }
-
-    @Test
-    public void writeChunk_writesChunkToWriter() throws Exception {
-        mBackupWriter.writeChunk(0, 10);
-
-        verify(mDiffScriptWriter).writeChunk(0, 10);
-    }
-
-    @Test
-    public void getBytesWritten_returnsTotalSum() throws Exception {
-        mBackupWriter.writeBytes(TEST_BYTES);
-        mBackupWriter.writeBytes(TEST_BYTES);
-        mBackupWriter.writeChunk(/*start=*/ 0, /*length=*/ 10);
-
-        long bytesWritten = mBackupWriter.getBytesWritten();
-
-        assertThat(bytesWritten).isEqualTo(2 * TEST_BYTES.length + 10);
-    }
-
-    @Test
-    public void flush_flushesWriter() throws IOException {
-        mBackupWriter.flush();
-
-        verify(mDiffScriptWriter).flush();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
deleted file mode 100644
index 325b601..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-
-import com.google.common.primitives.Bytes;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.util.Arrays;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class EncryptedChunkTest {
-    private static final byte[] CHUNK_HASH_1_BYTES =
-            Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES);
-    private static final byte[] NONCE_1 =
-            Arrays.copyOf(new byte[] {2}, EncryptedChunk.NONCE_LENGTH_BYTES);
-    private static final byte[] ENCRYPTED_BYTES_1 =
-            Arrays.copyOf(new byte[] {3}, EncryptedChunk.KEY_LENGTH_BYTES);
-
-    private static final byte[] CHUNK_HASH_2_BYTES =
-            Arrays.copyOf(new byte[] {4}, ChunkHash.HASH_LENGTH_BYTES);
-    private static final byte[] NONCE_2 =
-            Arrays.copyOf(new byte[] {5}, EncryptedChunk.NONCE_LENGTH_BYTES);
-    private static final byte[] ENCRYPTED_BYTES_2 =
-            Arrays.copyOf(new byte[] {6}, EncryptedChunk.KEY_LENGTH_BYTES);
-
-    @Test
-    public void testCreate_withIncorrectLength_throwsException() {
-        ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
-        byte[] shortNonce = Arrays.copyOf(new byte[] {2}, EncryptedChunk.NONCE_LENGTH_BYTES - 1);
-
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> EncryptedChunk.create(chunkHash, shortNonce, ENCRYPTED_BYTES_1));
-    }
-
-    @Test
-    public void testEncryptedBytes_forNewlyCreatedObject_returnsCorrectValue() {
-        ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
-        EncryptedChunk encryptedChunk =
-                EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1);
-
-        byte[] returnedBytes = encryptedChunk.encryptedBytes();
-
-        assertThat(returnedBytes)
-                .asList()
-                .containsExactlyElementsIn(Bytes.asList(ENCRYPTED_BYTES_1))
-                .inOrder();
-    }
-
-    @Test
-    public void testKey_forNewlyCreatedObject_returnsCorrectValue() {
-        ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
-        EncryptedChunk encryptedChunk =
-                EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1);
-
-        ChunkHash returnedKey = encryptedChunk.key();
-
-        assertThat(returnedKey).isEqualTo(chunkHash);
-    }
-
-    @Test
-    public void testNonce_forNewlycreatedObject_returnCorrectValue() {
-        ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
-        EncryptedChunk encryptedChunk =
-                EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1);
-
-        byte[] returnedNonce = encryptedChunk.nonce();
-
-        assertThat(returnedNonce).asList().containsExactlyElementsIn(Bytes.asList(NONCE_1));
-    }
-
-    @Test
-    public void testEquals() {
-        ChunkHash chunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
-        ChunkHash equalChunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
-        ChunkHash chunkHash2 = new ChunkHash(CHUNK_HASH_2_BYTES);
-        EncryptedChunk encryptedChunk1 =
-                EncryptedChunk.create(chunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
-        EncryptedChunk equalEncryptedChunk1 =
-                EncryptedChunk.create(equalChunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
-        EncryptedChunk encryptedChunk2 =
-                EncryptedChunk.create(chunkHash2, NONCE_2, ENCRYPTED_BYTES_2);
-
-        assertThat(encryptedChunk1).isEqualTo(equalEncryptedChunk1);
-        assertThat(encryptedChunk1).isNotEqualTo(encryptedChunk2);
-    }
-
-    @Test
-    public void testHashCode() {
-        ChunkHash chunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
-        ChunkHash equalChunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
-        ChunkHash chunkHash2 = new ChunkHash(CHUNK_HASH_2_BYTES);
-        EncryptedChunk encryptedChunk1 =
-                EncryptedChunk.create(chunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
-        EncryptedChunk equalEncryptedChunk1 =
-                EncryptedChunk.create(equalChunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
-        EncryptedChunk encryptedChunk2 =
-                EncryptedChunk.create(chunkHash2, NONCE_2, ENCRYPTED_BYTES_2);
-
-        int hash1 = encryptedChunk1.hashCode();
-        int equalHash1 = equalEncryptedChunk1.hashCode();
-        int hash2 = encryptedChunk2.hashCode();
-
-        assertThat(hash1).isEqualTo(equalHash1);
-        assertThat(hash1).isNotEqualTo(hash2);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
deleted file mode 100644
index 7e1fded..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.robolectric.RobolectricTestRunner;
-
-import java.util.Arrays;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class InlineLengthsEncryptedChunkEncoderTest {
-
-    private static final byte[] TEST_NONCE =
-            Arrays.copyOf(new byte[] {1}, EncryptedChunk.NONCE_LENGTH_BYTES);
-    private static final byte[] TEST_KEY_DATA =
-            Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES);
-    private static final byte[] TEST_DATA = {5, 4, 5, 7, 10, 12, 1, 2, 9};
-
-    @Mock private BackupWriter mMockBackupWriter;
-    private ChunkHash mTestKey;
-    private EncryptedChunk mTestChunk;
-    private EncryptedChunkEncoder mEncoder;
-
-    @Before
-    public void setUp() throws Exception {
-        mMockBackupWriter = mock(BackupWriter.class);
-        mTestKey = new ChunkHash(TEST_KEY_DATA);
-        mTestChunk = EncryptedChunk.create(mTestKey, TEST_NONCE, TEST_DATA);
-        mEncoder = new InlineLengthsEncryptedChunkEncoder();
-    }
-
-    @Test
-    public void writeChunkToWriter_writesLengthThenNonceThenData() throws Exception {
-        mEncoder.writeChunkToWriter(mMockBackupWriter, mTestChunk);
-
-        InOrder inOrder = inOrder(mMockBackupWriter);
-        inOrder.verify(mMockBackupWriter)
-                .writeBytes(
-                        InlineLengthsEncryptedChunkEncoder.toByteArray(
-                                TEST_NONCE.length + TEST_DATA.length));
-        inOrder.verify(mMockBackupWriter).writeBytes(TEST_NONCE);
-        inOrder.verify(mMockBackupWriter).writeBytes(TEST_DATA);
-    }
-
-    @Test
-    public void getEncodedLengthOfChunk_returnsSumOfNonceAndDataLengths() {
-        int encodedLength = mEncoder.getEncodedLengthOfChunk(mTestChunk);
-
-        assertThat(encodedLength).isEqualTo(Integer.BYTES + TEST_NONCE.length + TEST_DATA.length);
-    }
-
-    @Test
-    public void getChunkOrderingType_returnsExplicitStartsType() {
-        assertThat(mEncoder.getChunkOrderingType()).isEqualTo(ChunksMetadataProto.INLINE_LENGTHS);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
deleted file mode 100644
index 6f58ee1..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.robolectric.RobolectricTestRunner;
-
-import java.util.Arrays;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class LengthlessEncryptedChunkEncoderTest {
-    private static final byte[] TEST_NONCE =
-            Arrays.copyOf(new byte[] {1}, EncryptedChunk.NONCE_LENGTH_BYTES);
-    private static final byte[] TEST_KEY_DATA =
-            Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES);
-    private static final byte[] TEST_DATA = {5, 4, 5, 7, 10, 12, 1, 2, 9};
-
-    @Mock private BackupWriter mMockBackupWriter;
-    private ChunkHash mTestKey;
-    private EncryptedChunk mTestChunk;
-    private EncryptedChunkEncoder mEncoder;
-
-    @Before
-    public void setUp() throws Exception {
-        mMockBackupWriter = mock(BackupWriter.class);
-        mTestKey = new ChunkHash(TEST_KEY_DATA);
-        mTestChunk = EncryptedChunk.create(mTestKey, TEST_NONCE, TEST_DATA);
-        mEncoder = new LengthlessEncryptedChunkEncoder();
-    }
-
-    @Test
-    public void writeChunkToWriter_writesNonceThenData() throws Exception {
-        mEncoder.writeChunkToWriter(mMockBackupWriter, mTestChunk);
-
-        InOrder inOrder = inOrder(mMockBackupWriter);
-        inOrder.verify(mMockBackupWriter).writeBytes(TEST_NONCE);
-        inOrder.verify(mMockBackupWriter).writeBytes(TEST_DATA);
-    }
-
-    @Test
-    public void getEncodedLengthOfChunk_returnsSumOfNonceAndDataLengths() {
-        int encodedLength = mEncoder.getEncodedLengthOfChunk(mTestChunk);
-
-        assertThat(encodedLength).isEqualTo(TEST_NONCE.length + TEST_DATA.length);
-    }
-
-    @Test
-    public void getChunkOrderingType_returnsExplicitStartsType() {
-        assertThat(mEncoder.getChunkOrderingType()).isEqualTo(ChunksMetadataProto.EXPLICIT_STARTS);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ProtoStoreTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ProtoStoreTest.java
deleted file mode 100644
index d73c8e4..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ProtoStoreTest.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * 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.encryption.chunking;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.Context;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
-
-import com.google.common.collect.ImmutableMap;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Optional;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class ProtoStoreTest {
-    private static final String TEST_KEY_1 = "test_key_1";
-    private static final ChunkHash TEST_HASH_1 =
-            new ChunkHash(Arrays.copyOf(new byte[] {1}, EncryptedChunk.KEY_LENGTH_BYTES));
-    private static final ChunkHash TEST_HASH_2 =
-            new ChunkHash(Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES));
-    private static final int TEST_LENGTH_1 = 10;
-    private static final int TEST_LENGTH_2 = 18;
-
-    private static final String TEST_PACKAGE_1 = "com.example.test1";
-    private static final String TEST_PACKAGE_2 = "com.example.test2";
-
-    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
-    private File mStoreFolder;
-    private ProtoStore<ChunksMetadataProto.ChunkListing> mProtoStore;
-
-    @Before
-    public void setUp() throws Exception {
-        mStoreFolder = mTemporaryFolder.newFolder();
-        mProtoStore = new ProtoStore<>(ChunksMetadataProto.ChunkListing.class, mStoreFolder);
-    }
-
-    @Test
-    public void differentStoreTypes_operateSimultaneouslyWithoutInterfering() throws Exception {
-        ChunksMetadataProto.ChunkListing chunkListing =
-                createChunkListing(ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1));
-        KeyValueListingProto.KeyValueListing keyValueListing =
-                new KeyValueListingProto.KeyValueListing();
-        keyValueListing.entries = new KeyValueListingProto.KeyValueEntry[1];
-        keyValueListing.entries[0] = new KeyValueListingProto.KeyValueEntry();
-        keyValueListing.entries[0].key = TEST_KEY_1;
-        keyValueListing.entries[0].hash = TEST_HASH_1.getHash();
-
-        Context application = ApplicationProvider.getApplicationContext();
-        ProtoStore<ChunksMetadataProto.ChunkListing> chunkListingStore =
-                ProtoStore.createChunkListingStore(application);
-        ProtoStore<KeyValueListingProto.KeyValueListing> keyValueListingStore =
-                ProtoStore.createKeyValueListingStore(application);
-
-        chunkListingStore.saveProto(TEST_PACKAGE_1, chunkListing);
-        keyValueListingStore.saveProto(TEST_PACKAGE_1, keyValueListing);
-
-        ChunksMetadataProto.ChunkListing actualChunkListing =
-                chunkListingStore.loadProto(TEST_PACKAGE_1).get();
-        KeyValueListingProto.KeyValueListing actualKeyValueListing =
-                keyValueListingStore.loadProto(TEST_PACKAGE_1).get();
-        assertListingsEqual(actualChunkListing, chunkListing);
-        assertThat(actualKeyValueListing.entries.length).isEqualTo(1);
-        assertThat(actualKeyValueListing.entries[0].key).isEqualTo(TEST_KEY_1);
-        assertThat(actualKeyValueListing.entries[0].hash).isEqualTo(TEST_HASH_1.getHash());
-    }
-
-    @Test
-    public void construct_storeLocationIsFile_throws() throws Exception {
-        assertThrows(
-                IOException.class,
-                () ->
-                        new ProtoStore<>(
-                                ChunksMetadataProto.ChunkListing.class,
-                                mTemporaryFolder.newFile()));
-    }
-
-    @Test
-    public void loadChunkListing_noListingExists_returnsEmptyListing() throws Exception {
-        Optional<ChunksMetadataProto.ChunkListing> chunkListing =
-                mProtoStore.loadProto(TEST_PACKAGE_1);
-        assertThat(chunkListing.isPresent()).isFalse();
-    }
-
-    @Test
-    public void loadChunkListing_listingExists_returnsExistingListing() throws Exception {
-        ChunksMetadataProto.ChunkListing expected =
-                createChunkListing(
-                        ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1, TEST_HASH_2, TEST_LENGTH_2));
-        mProtoStore.saveProto(TEST_PACKAGE_1, expected);
-
-        ChunksMetadataProto.ChunkListing result = mProtoStore.loadProto(TEST_PACKAGE_1).get();
-
-        assertListingsEqual(result, expected);
-    }
-
-    @Test
-    public void loadProto_emptyPackageName_throwsException() throws Exception {
-        assertThrows(IllegalArgumentException.class, () -> mProtoStore.loadProto(""));
-    }
-
-    @Test
-    public void loadProto_nullPackageName_throwsException() throws Exception {
-        assertThrows(IllegalArgumentException.class, () -> mProtoStore.loadProto(null));
-    }
-
-    @Test
-    public void loadProto_packageNameContainsSlash_throwsException() throws Exception {
-        assertThrows(
-                IllegalArgumentException.class, () -> mProtoStore.loadProto(TEST_PACKAGE_1 + "/"));
-    }
-
-    @Test
-    public void saveProto_persistsToNewInstance() throws Exception {
-        ChunksMetadataProto.ChunkListing expected =
-                createChunkListing(
-                        ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1, TEST_HASH_2, TEST_LENGTH_2));
-        mProtoStore.saveProto(TEST_PACKAGE_1, expected);
-        mProtoStore = new ProtoStore<>(ChunksMetadataProto.ChunkListing.class, mStoreFolder);
-
-        ChunksMetadataProto.ChunkListing result = mProtoStore.loadProto(TEST_PACKAGE_1).get();
-
-        assertListingsEqual(result, expected);
-    }
-
-    @Test
-    public void saveProto_emptyPackageName_throwsException() throws Exception {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> mProtoStore.saveProto("", new ChunksMetadataProto.ChunkListing()));
-    }
-
-    @Test
-    public void saveProto_nullPackageName_throwsException() throws Exception {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> mProtoStore.saveProto(null, new ChunksMetadataProto.ChunkListing()));
-    }
-
-    @Test
-    public void saveProto_packageNameContainsSlash_throwsException() throws Exception {
-        assertThrows(
-                IllegalArgumentException.class,
-                () ->
-                        mProtoStore.saveProto(
-                                TEST_PACKAGE_1 + "/", new ChunksMetadataProto.ChunkListing()));
-    }
-
-    @Test
-    public void saveProto_nullListing_throwsException() throws Exception {
-        assertThrows(NullPointerException.class, () -> mProtoStore.saveProto(TEST_PACKAGE_1, null));
-    }
-
-    @Test
-    public void deleteProto_noListingExists_doesNothing() throws Exception {
-        ChunksMetadataProto.ChunkListing listing =
-                createChunkListing(ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1));
-        mProtoStore.saveProto(TEST_PACKAGE_1, listing);
-
-        mProtoStore.deleteProto(TEST_PACKAGE_2);
-
-        assertThat(mProtoStore.loadProto(TEST_PACKAGE_1).get().chunks.length).isEqualTo(1);
-    }
-
-    @Test
-    public void deleteProto_listingExists_deletesListing() throws Exception {
-        ChunksMetadataProto.ChunkListing listing =
-                createChunkListing(ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1));
-        mProtoStore.saveProto(TEST_PACKAGE_1, listing);
-
-        mProtoStore.deleteProto(TEST_PACKAGE_1);
-
-        assertThat(mProtoStore.loadProto(TEST_PACKAGE_1).isPresent()).isFalse();
-    }
-
-    @Test
-    public void deleteAllProtos_deletesAllProtos() throws Exception {
-        ChunksMetadataProto.ChunkListing listing1 =
-                createChunkListing(ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1));
-        ChunksMetadataProto.ChunkListing listing2 =
-                createChunkListing(ImmutableMap.of(TEST_HASH_2, TEST_LENGTH_2));
-        mProtoStore.saveProto(TEST_PACKAGE_1, listing1);
-        mProtoStore.saveProto(TEST_PACKAGE_2, listing2);
-
-        mProtoStore.deleteAllProtos();
-
-        assertThat(mProtoStore.loadProto(TEST_PACKAGE_1).isPresent()).isFalse();
-        assertThat(mProtoStore.loadProto(TEST_PACKAGE_2).isPresent()).isFalse();
-    }
-
-    @Test
-    public void deleteAllProtos_folderDeleted_doesNotCrash() throws Exception {
-        mStoreFolder.delete();
-
-        mProtoStore.deleteAllProtos();
-    }
-
-    private static ChunksMetadataProto.ChunkListing createChunkListing(
-            ImmutableMap<ChunkHash, Integer> chunks) {
-        ChunksMetadataProto.ChunkListing listing = new ChunksMetadataProto.ChunkListing();
-        listing.cipherType = ChunksMetadataProto.AES_256_GCM;
-        listing.chunkOrderingType = ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
-
-        List<ChunksMetadataProto.Chunk> chunkProtos = new ArrayList<>();
-        for (Entry<ChunkHash, Integer> entry : chunks.entrySet()) {
-            ChunksMetadataProto.Chunk chunk = new ChunksMetadataProto.Chunk();
-            chunk.hash = entry.getKey().getHash();
-            chunk.length = entry.getValue();
-            chunkProtos.add(chunk);
-        }
-        listing.chunks = chunkProtos.toArray(new ChunksMetadataProto.Chunk[0]);
-        return listing;
-    }
-
-    private void assertListingsEqual(
-            ChunksMetadataProto.ChunkListing result, ChunksMetadataProto.ChunkListing expected) {
-        assertThat(result.chunks.length).isEqualTo(expected.chunks.length);
-        for (int i = 0; i < result.chunks.length; i++) {
-            assertWithMessage("Chunk " + i)
-                    .that(result.chunks[i].length)
-                    .isEqualTo(expected.chunks[i].length);
-            assertWithMessage("Chunk " + i)
-                    .that(result.chunks[i].hash)
-                    .isEqualTo(expected.chunks[i].hash);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
deleted file mode 100644
index 966d3e2..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.google.common.primitives.Bytes;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.ByteArrayOutputStream;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class RawBackupWriterTest {
-    private static final byte[] TEST_BYTES = {1, 2, 3, 4, 5, 6};
-
-    private BackupWriter mWriter;
-    private ByteArrayOutputStream mOutput;
-
-    @Before
-    public void setUp() {
-        mOutput = new ByteArrayOutputStream();
-        mWriter = new RawBackupWriter(mOutput);
-    }
-
-    @Test
-    public void writeBytes_writesToOutputStream() throws Exception {
-        mWriter.writeBytes(TEST_BYTES);
-
-        assertThat(mOutput.toByteArray())
-                .asList()
-                .containsExactlyElementsIn(Bytes.asList(TEST_BYTES))
-                .inOrder();
-    }
-
-    @Test
-    public void writeChunk_throwsUnsupportedOperationException() throws Exception {
-        assertThrows(UnsupportedOperationException.class, () -> mWriter.writeChunk(0, 0));
-    }
-
-    @Test
-    public void getBytesWritten_returnsTotalSum() throws Exception {
-        mWriter.writeBytes(TEST_BYTES);
-        mWriter.writeBytes(TEST_BYTES);
-
-        long bytesWritten = mWriter.getBytesWritten();
-
-        assertThat(bytesWritten).isEqualTo(2 * TEST_BYTES.length);
-    }
-
-    @Test
-    public void flush_flushesOutputStream() throws Exception {
-        mOutput = mock(ByteArrayOutputStream.class);
-        mWriter = new RawBackupWriter(mOutput);
-
-        mWriter.flush();
-
-        verify(mOutput).flush();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
deleted file mode 100644
index 73baf80..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Locale;
-
-/** Tests for {@link SingleStreamDiffScriptWriter}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class SingleStreamDiffScriptWriterTest {
-    private static final int MAX_CHUNK_SIZE_IN_BYTES = 256;
-    /** By default this Locale does not use Arabic numbers for %d formatting. */
-    private static final Locale HINDI = new Locale("hi", "IN");
-
-    private Locale mDefaultLocale;
-    private ByteArrayOutputStream mOutputStream;
-    private SingleStreamDiffScriptWriter mDiffScriptWriter;
-
-    @Before
-    public void setUp() {
-        mDefaultLocale = Locale.getDefault();
-        mOutputStream = new ByteArrayOutputStream();
-        mDiffScriptWriter =
-                new SingleStreamDiffScriptWriter(mOutputStream, MAX_CHUNK_SIZE_IN_BYTES);
-    }
-
-    @After
-    public void tearDown() {
-        Locale.setDefault(mDefaultLocale);
-    }
-
-    @Test
-    public void writeChunk_withNegativeStart_throwsException() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> mDiffScriptWriter.writeChunk(-1, 50));
-    }
-
-    @Test
-    public void writeChunk_withZeroLength_throwsException() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> mDiffScriptWriter.writeChunk(0, 0));
-    }
-
-    @Test
-    public void writeChunk_withExistingBytesInBuffer_writesBufferFirst()
-            throws IOException {
-        String testString = "abcd";
-        writeStringAsBytesToWriter(testString, mDiffScriptWriter);
-
-        mDiffScriptWriter.writeChunk(0, 20);
-        mDiffScriptWriter.flush();
-
-        // Expected format: length of abcd, newline, abcd, newline, chunk start - chunk end
-        assertThat(mOutputStream.toString("UTF-8")).isEqualTo(
-                String.format("%d\n%s\n%d-%d\n", testString.length(), testString, 0, 19));
-    }
-
-    @Test
-    public void writeChunk_overlappingPreviousChunk_combinesChunks() throws IOException {
-        mDiffScriptWriter.writeChunk(3, 4);
-
-        mDiffScriptWriter.writeChunk(7, 5);
-        mDiffScriptWriter.flush();
-
-        assertThat(mOutputStream.toString("UTF-8")).isEqualTo(String.format("3-11\n"));
-    }
-
-    @Test
-    public void writeChunk_formatsByteIndexesUsingArabicNumbers() throws Exception {
-        Locale.setDefault(HINDI);
-
-        mDiffScriptWriter.writeChunk(0, 12345);
-        mDiffScriptWriter.flush();
-
-        assertThat(mOutputStream.toString("UTF-8")).isEqualTo("0-12344\n");
-    }
-
-    @Test
-    public void flush_flushesOutputStream() throws IOException {
-        ByteArrayOutputStream mockOutputStream = mock(ByteArrayOutputStream.class);
-        SingleStreamDiffScriptWriter diffScriptWriter =
-                new SingleStreamDiffScriptWriter(mockOutputStream, MAX_CHUNK_SIZE_IN_BYTES);
-
-        diffScriptWriter.flush();
-
-        verify(mockOutputStream).flush();
-    }
-
-    private void writeStringAsBytesToWriter(String string, SingleStreamDiffScriptWriter writer)
-            throws IOException {
-        byte[] bytes = string.getBytes("UTF-8");
-        for (int i = 0; i < bytes.length; i++) {
-            writer.writeByte(bytes[i]);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
deleted file mode 100644
index 77b7347..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking.cdc;
-
-import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Random;
-
-import javax.crypto.SecretKey;
-
-/** Tests for {@link ContentDefinedChunker}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class ContentDefinedChunkerTest {
-    private static final int WINDOW_SIZE_BYTES = 31;
-    private static final int MIN_SIZE_BYTES = 40;
-    private static final int MAX_SIZE_BYTES = 300;
-    private static final String CHUNK_BOUNDARY = "<----------BOUNDARY----------->";
-    private static final byte[] CHUNK_BOUNDARY_BYTES = CHUNK_BOUNDARY.getBytes(UTF_8);
-    private static final String CHUNK_1 = "This is the first chunk";
-    private static final String CHUNK_2 = "And this is the second chunk";
-    private static final String CHUNK_3 = "And finally here is the third chunk";
-    private static final String SMALL_CHUNK = "12345678";
-
-    private FingerprintMixer mFingerprintMixer;
-    private RabinFingerprint64 mRabinFingerprint64;
-    private ContentDefinedChunker mChunker;
-
-    /** Set up a {@link ContentDefinedChunker} and dependencies for use in the tests. */
-    @Before
-    public void setUp() throws Exception {
-        SecretKey secretKey = generateAesKey();
-        byte[] salt = new byte[FingerprintMixer.SALT_LENGTH_BYTES];
-        Random random = new Random();
-        random.nextBytes(salt);
-        mFingerprintMixer = new FingerprintMixer(secretKey, salt);
-
-        mRabinFingerprint64 = new RabinFingerprint64();
-        long chunkBoundaryFingerprint = calculateFingerprint(CHUNK_BOUNDARY_BYTES);
-        mChunker =
-                new ContentDefinedChunker(
-                        MIN_SIZE_BYTES,
-                        MAX_SIZE_BYTES,
-                        mRabinFingerprint64,
-                        mFingerprintMixer,
-                        (fingerprint) -> fingerprint == chunkBoundaryFingerprint);
-    }
-
-    /**
-     * Creating a {@link ContentDefinedChunker} with a minimum chunk size that is smaller than the
-     * window size should throw an {@link IllegalArgumentException}.
-     */
-    @Test
-    public void create_withMinChunkSizeSmallerThanWindowSize_throwsIllegalArgumentException() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () ->
-                        new ContentDefinedChunker(
-                                WINDOW_SIZE_BYTES - 1,
-                                MAX_SIZE_BYTES,
-                                mRabinFingerprint64,
-                                mFingerprintMixer,
-                                null));
-    }
-
-    /**
-     * Creating a {@link ContentDefinedChunker} with a maximum chunk size that is smaller than the
-     * minimum chunk size should throw an {@link IllegalArgumentException}.
-     */
-    @Test
-    public void create_withMaxChunkSizeSmallerThanMinChunkSize_throwsIllegalArgumentException() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () ->
-                        new ContentDefinedChunker(
-                                MIN_SIZE_BYTES,
-                                MIN_SIZE_BYTES - 1,
-                                mRabinFingerprint64,
-                                mFingerprintMixer,
-                                null));
-    }
-
-    /**
-     * {@link ContentDefinedChunker#chunkify(InputStream, Chunker.ChunkConsumer)} should split the
-     * input stream across chunk boundaries by default.
-     */
-    @Test
-    public void chunkify_withLargeChunks_splitsIntoChunksAcrossBoundaries() throws Exception {
-        byte[] input =
-                (CHUNK_1 + CHUNK_BOUNDARY + CHUNK_2 + CHUNK_BOUNDARY + CHUNK_3).getBytes(UTF_8);
-        ByteArrayInputStream inputStream = new ByteArrayInputStream(input);
-        ArrayList<String> result = new ArrayList<>();
-
-        mChunker.chunkify(inputStream, (chunk) -> result.add(new String(chunk, UTF_8)));
-
-        assertThat(result)
-                .containsExactly(CHUNK_1 + CHUNK_BOUNDARY, CHUNK_2 + CHUNK_BOUNDARY, CHUNK_3)
-                .inOrder();
-    }
-
-    /** Chunks should be combined across boundaries until they reach the minimum chunk size. */
-    @Test
-    public void chunkify_withSmallChunks_combinesChunksUntilMinSize() throws Exception {
-        byte[] input =
-                (SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2 + CHUNK_BOUNDARY + CHUNK_3).getBytes(UTF_8);
-        ByteArrayInputStream inputStream = new ByteArrayInputStream(input);
-        ArrayList<String> result = new ArrayList<>();
-
-        mChunker.chunkify(inputStream, (chunk) -> result.add(new String(chunk, UTF_8)));
-
-        assertThat(result)
-                .containsExactly(SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2 + CHUNK_BOUNDARY, CHUNK_3)
-                .inOrder();
-        assertThat(result.get(0).length()).isAtLeast(MIN_SIZE_BYTES);
-    }
-
-    /** Chunks can not be larger than the maximum chunk size. */
-    @Test
-    public void chunkify_doesNotProduceChunksLargerThanMaxSize() throws Exception {
-        byte[] largeInput = new byte[MAX_SIZE_BYTES * 10];
-        Arrays.fill(largeInput, "a".getBytes(UTF_8)[0]);
-        ByteArrayInputStream inputStream = new ByteArrayInputStream(largeInput);
-        ArrayList<String> result = new ArrayList<>();
-
-        mChunker.chunkify(inputStream, (chunk) -> result.add(new String(chunk, UTF_8)));
-
-        byte[] expectedChunkBytes = new byte[MAX_SIZE_BYTES];
-        Arrays.fill(expectedChunkBytes, "a".getBytes(UTF_8)[0]);
-        String expectedChunk = new String(expectedChunkBytes, UTF_8);
-        assertThat(result)
-                .containsExactly(
-                        expectedChunk,
-                        expectedChunk,
-                        expectedChunk,
-                        expectedChunk,
-                        expectedChunk,
-                        expectedChunk,
-                        expectedChunk,
-                        expectedChunk,
-                        expectedChunk,
-                        expectedChunk)
-                .inOrder();
-    }
-
-    /**
-     * If the input stream signals zero availablility, {@link
-     * ContentDefinedChunker#chunkify(InputStream, Chunker.ChunkConsumer)} should still work.
-     */
-    @Test
-    public void chunkify_withInputStreamReturningZeroAvailability_returnsChunks() throws Exception {
-        byte[] input = (SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2).getBytes(UTF_8);
-        ZeroAvailabilityInputStream zeroAvailabilityInputStream =
-                new ZeroAvailabilityInputStream(input);
-        ArrayList<String> result = new ArrayList<>();
-
-        mChunker.chunkify(
-                zeroAvailabilityInputStream, (chunk) -> result.add(new String(chunk, UTF_8)));
-
-        assertThat(result).containsExactly(SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2).inOrder();
-    }
-
-    /**
-     * {@link ContentDefinedChunker#chunkify(InputStream, Chunker.ChunkConsumer)} should rethrow any
-     * exception thrown by its consumer.
-     */
-    @Test
-    public void chunkify_whenConsumerThrowsException_rethrowsException() throws Exception {
-        ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] {1});
-
-        assertThrows(
-                GeneralSecurityException.class,
-                () ->
-                        mChunker.chunkify(
-                                inputStream,
-                                (chunk) -> {
-                                    throw new GeneralSecurityException();
-                                }));
-    }
-
-    private long calculateFingerprint(byte[] bytes) {
-        long fingerprint = 0;
-        for (byte inByte : bytes) {
-            fingerprint =
-                    mRabinFingerprint64.computeFingerprint64(
-                            /*inChar=*/ inByte, /*outChar=*/ (byte) 0, fingerprint);
-        }
-        return mFingerprintMixer.mix(fingerprint);
-    }
-
-    private static class ZeroAvailabilityInputStream extends ByteArrayInputStream {
-        ZeroAvailabilityInputStream(byte[] wrapped) {
-            super(wrapped);
-        }
-
-        @Override
-        public synchronized int available() {
-            return 0;
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
deleted file mode 100644
index 936b5dc..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking.cdc;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.util.HashSet;
-import java.util.Random;
-
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-/** Tests for {@link FingerprintMixer}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class FingerprintMixerTest {
-    private static final String KEY_ALGORITHM = "AES";
-    private static final int SEED = 42;
-    private static final int SALT_LENGTH_BYTES = 256 / 8;
-    private static final int KEY_SIZE_BITS = 256;
-
-    private Random mSeededRandom;
-    private FingerprintMixer mFingerprintMixer;
-
-    /** Set up a {@link FingerprintMixer} with deterministic key and salt generation. */
-    @Before
-    public void setUp() throws Exception {
-        // Seed so that the tests are deterministic.
-        mSeededRandom = new Random(SEED);
-        mFingerprintMixer = new FingerprintMixer(randomKey(), randomSalt());
-    }
-
-    /**
-     * Construcing a {@link FingerprintMixer} with a salt that is too small should throw an {@link
-     * IllegalArgumentException}.
-     */
-    @Test
-    public void create_withIncorrectSaltSize_throwsIllegalArgumentException() {
-        byte[] tooSmallSalt = new byte[SALT_LENGTH_BYTES - 1];
-
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> new FingerprintMixer(randomKey(), tooSmallSalt));
-    }
-
-    /**
-     * Constructing a {@link FingerprintMixer} with a secret key that can't be encoded should throw
-     * an {@link InvalidKeyException}.
-     */
-    @Test
-    public void create_withUnencodableSecretKey_throwsInvalidKeyException() {
-        byte[] keyBytes = new byte[KEY_SIZE_BITS / 8];
-        UnencodableSecretKeySpec keySpec =
-                new UnencodableSecretKeySpec(keyBytes, 0, keyBytes.length, KEY_ALGORITHM);
-
-        assertThrows(InvalidKeyException.class, () -> new FingerprintMixer(keySpec, randomSalt()));
-    }
-
-    /**
-     * {@link FingerprintMixer#getAddend()} should not return the same addend for two different
-     * keys.
-     */
-    @Test
-    public void getAddend_withDifferentKey_returnsDifferentResult() throws Exception {
-        int iterations = 100_000;
-        HashSet<Long> returnedAddends = new HashSet<>();
-        byte[] salt = randomSalt();
-
-        for (int i = 0; i < iterations; i++) {
-            FingerprintMixer fingerprintMixer = new FingerprintMixer(randomKey(), salt);
-            long addend = fingerprintMixer.getAddend();
-            returnedAddends.add(addend);
-        }
-
-        assertThat(returnedAddends).containsNoDuplicates();
-    }
-
-    /**
-     * {@link FingerprintMixer#getMultiplicand()} should not return the same multiplicand for two
-     * different keys.
-     */
-    @Test
-    public void getMultiplicand_withDifferentKey_returnsDifferentResult() throws Exception {
-        int iterations = 100_000;
-        HashSet<Long> returnedMultiplicands = new HashSet<>();
-        byte[] salt = randomSalt();
-
-        for (int i = 0; i < iterations; i++) {
-            FingerprintMixer fingerprintMixer = new FingerprintMixer(randomKey(), salt);
-            long multiplicand = fingerprintMixer.getMultiplicand();
-            returnedMultiplicands.add(multiplicand);
-        }
-
-        assertThat(returnedMultiplicands).containsNoDuplicates();
-    }
-
-    /** The multiplicant returned by {@link FingerprintMixer} should always be odd. */
-    @Test
-    public void getMultiplicand_isOdd() throws Exception {
-        int iterations = 100_000;
-
-        for (int i = 0; i < iterations; i++) {
-            FingerprintMixer fingerprintMixer = new FingerprintMixer(randomKey(), randomSalt());
-
-            long multiplicand = fingerprintMixer.getMultiplicand();
-
-            assertThat(isOdd(multiplicand)).isTrue();
-        }
-    }
-
-    /** {@link FingerprintMixer#mix(long)} should have a random distribution. */
-    @Test
-    public void mix_randomlyDistributesBits() throws Exception {
-        int iterations = 100_000;
-        float tolerance = 0.1f;
-        int[] totals = new int[64];
-
-        for (int i = 0; i < iterations; i++) {
-            long n = mFingerprintMixer.mix(mSeededRandom.nextLong());
-            for (int j = 0; j < 64; j++) {
-                int bit = (int) (n >> j & 1);
-                totals[j] += bit;
-            }
-        }
-
-        for (int i = 0; i < 64; i++) {
-            float mean = ((float) totals[i]) / iterations;
-            float diff = Math.abs(mean - 0.5f);
-            assertThat(diff).isLessThan(tolerance);
-        }
-    }
-
-    /**
-     * {@link FingerprintMixer#mix(long)} should always produce a number that's different from the
-     * input.
-     */
-    @Test
-    public void mix_doesNotProduceSameNumberAsInput() {
-        int iterations = 100_000;
-
-        for (int i = 0; i < iterations; i++) {
-            assertThat(mFingerprintMixer.mix(i)).isNotEqualTo(i);
-        }
-    }
-
-    private byte[] randomSalt() {
-        byte[] salt = new byte[SALT_LENGTH_BYTES];
-        mSeededRandom.nextBytes(salt);
-        return salt;
-    }
-
-    /**
-     * Not a secure way of generating keys. We want to deterministically generate the same keys for
-     * each test run, though, to ensure the test is deterministic.
-     */
-    private SecretKey randomKey() {
-        byte[] keyBytes = new byte[KEY_SIZE_BITS / 8];
-        mSeededRandom.nextBytes(keyBytes);
-        return new SecretKeySpec(keyBytes, 0, keyBytes.length, KEY_ALGORITHM);
-    }
-
-    private static boolean isOdd(long n) {
-        return Math.abs(n % 2) == 1;
-    }
-
-    /**
-     * Subclass of {@link SecretKeySpec} that does not provide an encoded version. As per its
-     * contract in {@link Key}, that means {@code getEncoded()} always returns null.
-     */
-    private class UnencodableSecretKeySpec extends SecretKeySpec {
-        UnencodableSecretKeySpec(byte[] key, int offset, int len, String algorithm) {
-            super(key, offset, len, algorithm);
-        }
-
-        @Override
-        public byte[] getEncoded() {
-            return null;
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
deleted file mode 100644
index 5494374..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking.cdc;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-/** Tests for {@link Hkdf}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class HkdfTest {
-    /** HKDF Test Case 1 IKM from RFC 5869 */
-    private static final byte[] HKDF_CASE1_IKM = {
-        0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-        0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-        0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-        0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-        0x0b, 0x0b
-    };
-
-    /** HKDF Test Case 1 salt from RFC 5869 */
-    private static final byte[] HKDF_CASE1_SALT = {
-        0x00, 0x01, 0x02, 0x03, 0x04,
-        0x05, 0x06, 0x07, 0x08, 0x09,
-        0x0a, 0x0b, 0x0c
-    };
-
-    /** HKDF Test Case 1 info from RFC 5869 */
-    private static final byte[] HKDF_CASE1_INFO = {
-        (byte) 0xf0, (byte) 0xf1, (byte) 0xf2, (byte) 0xf3, (byte) 0xf4,
-        (byte) 0xf5, (byte) 0xf6, (byte) 0xf7, (byte) 0xf8, (byte) 0xf9
-    };
-
-    /** First 32 bytes of HKDF Test Case 1 OKM (output) from RFC 5869 */
-    private static final byte[] HKDF_CASE1_OKM = {
-        (byte) 0x3c, (byte) 0xb2, (byte) 0x5f, (byte) 0x25, (byte) 0xfa,
-        (byte) 0xac, (byte) 0xd5, (byte) 0x7a, (byte) 0x90, (byte) 0x43,
-        (byte) 0x4f, (byte) 0x64, (byte) 0xd0, (byte) 0x36, (byte) 0x2f,
-        (byte) 0x2a, (byte) 0x2d, (byte) 0x2d, (byte) 0x0a, (byte) 0x90,
-        (byte) 0xcf, (byte) 0x1a, (byte) 0x5a, (byte) 0x4c, (byte) 0x5d,
-        (byte) 0xb0, (byte) 0x2d, (byte) 0x56, (byte) 0xec, (byte) 0xc4,
-        (byte) 0xc5, (byte) 0xbf
-    };
-
-    /** Test the example from RFC 5869. */
-    @Test
-    public void hkdf_derivesKeyMaterial() throws Exception {
-        byte[] result = Hkdf.hkdf(HKDF_CASE1_IKM, HKDF_CASE1_SALT, HKDF_CASE1_INFO);
-
-        assertThat(result).isEqualTo(HKDF_CASE1_OKM);
-    }
-
-    /** Providing a key that is null should throw a {@link java.lang.NullPointerException}. */
-    @Test
-    public void hkdf_withNullKey_throwsNullPointerException() throws Exception {
-        assertThrows(
-                NullPointerException.class,
-                () -> Hkdf.hkdf(null, HKDF_CASE1_SALT, HKDF_CASE1_INFO));
-    }
-
-    /** Providing a salt that is null should throw a {@link java.lang.NullPointerException}. */
-    @Test
-    public void hkdf_withNullSalt_throwsNullPointerException() throws Exception {
-        assertThrows(
-                NullPointerException.class, () -> Hkdf.hkdf(HKDF_CASE1_IKM, null, HKDF_CASE1_INFO));
-    }
-
-    /** Providing data that is null should throw a {@link java.lang.NullPointerException}. */
-    @Test
-    public void hkdf_withNullData_throwsNullPointerException() throws Exception {
-        assertThrows(
-                NullPointerException.class, () -> Hkdf.hkdf(HKDF_CASE1_IKM, HKDF_CASE1_SALT, null));
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
deleted file mode 100644
index 277dc37..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking.cdc;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.util.Random;
-
-/** Tests for {@link IsChunkBreakpoint}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class IsChunkBreakpointTest {
-    private static final int RANDOM_SEED = 42;
-    private static final double TOLERANCE = 0.01;
-    private static final int NUMBER_OF_TESTS = 10000;
-    private static final int BITS_PER_LONG = 64;
-
-    private Random mRandom;
-
-    /** Make sure that tests are deterministic. */
-    @Before
-    public void setUp() {
-        mRandom = new Random(RANDOM_SEED);
-    }
-
-    /**
-     * Providing a negative average number of trials should throw an {@link
-     * IllegalArgumentException}.
-     */
-    @Test
-    public void create_withNegativeAverageNumberOfTrials_throwsIllegalArgumentException() {
-        assertThrows(IllegalArgumentException.class, () -> new IsChunkBreakpoint(-1));
-    }
-
-    // Note: the following three tests are compute-intensive, so be cautious adding more.
-
-    /**
-     * If the provided average number of trials is zero, a breakpoint should be expected after one
-     * trial on average.
-     */
-    @Test
-    public void
-            isBreakpoint_withZeroAverageNumberOfTrials_isTrueOnAverageAfterOneTrial() {
-        assertExpectedTrials(new IsChunkBreakpoint(0), /*expectedTrials=*/ 1);
-    }
-
-    /**
-     * If the provided average number of trials is 512, a breakpoint should be expected after 512
-     * trials on average.
-     */
-    @Test
-    public void
-            isBreakpoint_with512AverageNumberOfTrials_isTrueOnAverageAfter512Trials() {
-        assertExpectedTrials(new IsChunkBreakpoint(512), /*expectedTrials=*/ 512);
-    }
-
-    /**
-     * If the provided average number of trials is 1024, a breakpoint should be expected after 1024
-     * trials on average.
-     */
-    @Test
-    public void
-            isBreakpoint_with1024AverageNumberOfTrials_isTrueOnAverageAfter1024Trials() {
-        assertExpectedTrials(new IsChunkBreakpoint(1024), /*expectedTrials=*/ 1024);
-    }
-
-    /** The number of leading zeros should be the logarithm of the average number of trials. */
-    @Test
-    public void getLeadingZeros_squaredIsAverageNumberOfTrials() {
-        for (int i = 0; i < BITS_PER_LONG; i++) {
-            long averageNumberOfTrials = (long) Math.pow(2, i);
-
-            int leadingZeros = new IsChunkBreakpoint(averageNumberOfTrials).getLeadingZeros();
-
-            assertThat(leadingZeros).isEqualTo(i);
-        }
-    }
-
-    private void assertExpectedTrials(IsChunkBreakpoint isChunkBreakpoint, long expectedTrials) {
-        long sum = 0;
-        for (int i = 0; i < NUMBER_OF_TESTS; i++) {
-            sum += numberOfTrialsTillBreakpoint(isChunkBreakpoint);
-        }
-        long averageTrials = sum / NUMBER_OF_TESTS;
-        assertThat((double) Math.abs(averageTrials - expectedTrials))
-                .isLessThan(TOLERANCE * expectedTrials);
-    }
-
-    private int numberOfTrialsTillBreakpoint(IsChunkBreakpoint isChunkBreakpoint) {
-        int trials = 0;
-
-        while (true) {
-            trials++;
-            if (isChunkBreakpoint.isBreakpoint(mRandom.nextLong())) {
-                return trials;
-            }
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
deleted file mode 100644
index 729580c..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.chunking.cdc;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-/** Tests for {@link RabinFingerprint64}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class RabinFingerprint64Test {
-    private static final int WINDOW_SIZE = 31;
-    private static final ImmutableList<String> TEST_STRINGS =
-            ImmutableList.of(
-                    "ervHTtChYXO6eXivYqThlyyzqkbRaOR",
-                    "IxaVunH9ZC3qneWfhj1GkBH4ys9CYqz",
-                    "wZRVjlE1p976icCFPX9pibk4PEBvjSH",
-                    "pHIVaT8x8If9D6s9croksgNmJpmGYWI");
-
-    private final RabinFingerprint64 mRabinFingerprint64 = new RabinFingerprint64();
-
-    /**
-     * No matter where in the input buffer a string occurs, {@link
-     * RabinFingerprint64#computeFingerprint64(byte, byte, long)} should return the same
-     * fingerprint.
-     */
-    @Test
-    public void computeFingerprint64_forSameWindow_returnsSameFingerprint() {
-        long fingerprint1 =
-                computeFingerprintAtPosition(getBytes(TEST_STRINGS.get(0)), WINDOW_SIZE - 1);
-        long fingerprint2 =
-                computeFingerprintAtPosition(
-                        getBytes(TEST_STRINGS.get(1), TEST_STRINGS.get(0)), WINDOW_SIZE * 2 - 1);
-        long fingerprint3 =
-                computeFingerprintAtPosition(
-                        getBytes(TEST_STRINGS.get(2), TEST_STRINGS.get(3), TEST_STRINGS.get(0)),
-                        WINDOW_SIZE * 3 - 1);
-        String stub = "abc";
-        long fingerprint4 =
-                computeFingerprintAtPosition(
-                        getBytes(stub, TEST_STRINGS.get(0)), WINDOW_SIZE + stub.length() - 1);
-
-        // Assert that all fingerprints are exactly the same
-        assertThat(ImmutableSet.of(fingerprint1, fingerprint2, fingerprint3, fingerprint4))
-                .hasSize(1);
-    }
-
-    /** The computed fingerprint should be different for different inputs. */
-    @Test
-    public void computeFingerprint64_withDifferentInput_returnsDifferentFingerprint() {
-        long fingerprint1 = computeFingerprintOf(TEST_STRINGS.get(0));
-        long fingerprint2 = computeFingerprintOf(TEST_STRINGS.get(1));
-        long fingerprint3 = computeFingerprintOf(TEST_STRINGS.get(2));
-        long fingerprint4 = computeFingerprintOf(TEST_STRINGS.get(3));
-
-        assertThat(ImmutableList.of(fingerprint1, fingerprint2, fingerprint3, fingerprint4))
-                .containsNoDuplicates();
-    }
-
-    /**
-     * An input with the same characters in a different order should return a different fingerprint.
-     */
-    @Test
-    public void computeFingerprint64_withSameInputInDifferentOrder_returnsDifferentFingerprint() {
-        long fingerprint1 = computeFingerprintOf("abcdefghijklmnopqrstuvwxyz12345");
-        long fingerprint2 = computeFingerprintOf("54321zyxwvutsrqponmlkjihgfedcba");
-        long fingerprint3 = computeFingerprintOf("4bcdefghijklmnopqrstuvwxyz123a5");
-        long fingerprint4 = computeFingerprintOf("bacdefghijklmnopqrstuvwxyz12345");
-
-        assertThat(ImmutableList.of(fingerprint1, fingerprint2, fingerprint3, fingerprint4))
-                .containsNoDuplicates();
-    }
-
-    /** UTF-8 bytes of all the given strings in order. */
-    private byte[] getBytes(String... strings) {
-        StringBuilder sb = new StringBuilder();
-        for (String s : strings) {
-            sb.append(s);
-        }
-        return sb.toString().getBytes(UTF_8);
-    }
-
-    /**
-     * The Rabin fingerprint of a window of bytes ending at {@code position} in the {@code bytes}
-     * array.
-     */
-    private long computeFingerprintAtPosition(byte[] bytes, int position) {
-        assertThat(position).isAtMost(bytes.length - 1);
-        long fingerprint = 0;
-        for (int i = 0; i <= position; i++) {
-            byte outChar;
-            if (i >= WINDOW_SIZE) {
-                outChar = bytes[i - WINDOW_SIZE];
-            } else {
-                outChar = (byte) 0;
-            }
-            fingerprint =
-                    mRabinFingerprint64.computeFingerprint64(
-                            /*inChar=*/ bytes[i], outChar, fingerprint);
-        }
-        return fingerprint;
-    }
-
-    private long computeFingerprintOf(String s) {
-        assertThat(s.length()).isEqualTo(WINDOW_SIZE);
-        return computeFingerprintAtPosition(s.getBytes(UTF_8), WINDOW_SIZE - 1);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/KeyWrapUtilsTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/KeyWrapUtilsTest.java
deleted file mode 100644
index b607404..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/KeyWrapUtilsTest.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.security.InvalidKeyException;
-
-import javax.crypto.SecretKey;
-
-/** Key wrapping tests */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class KeyWrapUtilsTest {
-    private static final int KEY_SIZE_BITS = 256;
-    private static final int BITS_PER_BYTE = 8;
-    private static final int GCM_NONCE_LENGTH_BYTES = 16;
-    private static final int GCM_TAG_LENGTH_BYTES = 16;
-
-    /** Test a wrapped key has metadata */
-    @Test
-    public void wrap_addsMetadata() throws Exception {
-        WrappedKeyProto.WrappedKey wrappedKey =
-                KeyWrapUtils.wrap(
-                        /*secondaryKey=*/ generateAesKey(), /*tertiaryKey=*/ generateAesKey());
-        assertThat(wrappedKey.metadata).isNotNull();
-        assertThat(wrappedKey.metadata.type).isEqualTo(WrappedKeyProto.KeyMetadata.AES_256_GCM);
-    }
-
-    /** Test a wrapped key has an algorithm specified */
-    @Test
-    public void wrap_addsWrapAlgorithm() throws Exception {
-        WrappedKeyProto.WrappedKey wrappedKey =
-                KeyWrapUtils.wrap(
-                        /*secondaryKey=*/ generateAesKey(), /*tertiaryKey=*/ generateAesKey());
-        assertThat(wrappedKey.wrapAlgorithm).isEqualTo(WrappedKeyProto.WrappedKey.AES_256_GCM);
-    }
-
-    /** Test a wrapped key haas an nonce of the right length */
-    @Test
-    public void wrap_addsNonceOfAppropriateLength() throws Exception {
-        WrappedKeyProto.WrappedKey wrappedKey =
-                KeyWrapUtils.wrap(
-                        /*secondaryKey=*/ generateAesKey(), /*tertiaryKey=*/ generateAesKey());
-        assertThat(wrappedKey.nonce).hasLength(GCM_NONCE_LENGTH_BYTES);
-    }
-
-    /** Test a wrapped key has a key of the right length */
-    @Test
-    public void wrap_addsTagOfAppropriateLength() throws Exception {
-        WrappedKeyProto.WrappedKey wrappedKey =
-                KeyWrapUtils.wrap(
-                        /*secondaryKey=*/ generateAesKey(), /*tertiaryKey=*/ generateAesKey());
-        assertThat(wrappedKey.key).hasLength(KEY_SIZE_BITS / BITS_PER_BYTE + GCM_TAG_LENGTH_BYTES);
-    }
-
-    /** Ensure a key can be wrapped and unwrapped again */
-    @Test
-    public void unwrap_unwrapsEncryptedKey() throws Exception {
-        SecretKey secondaryKey = generateAesKey();
-        SecretKey tertiaryKey = generateAesKey();
-        WrappedKeyProto.WrappedKey wrappedKey = KeyWrapUtils.wrap(secondaryKey, tertiaryKey);
-        SecretKey unwrappedKey = KeyWrapUtils.unwrap(secondaryKey, wrappedKey);
-        assertThat(unwrappedKey).isEqualTo(tertiaryKey);
-    }
-
-    /** Ensure the unwrap method rejects keys with bad algorithms */
-    @Test(expected = InvalidKeyException.class)
-    public void unwrap_throwsForBadWrapAlgorithm() throws Exception {
-        SecretKey secondaryKey = generateAesKey();
-        WrappedKeyProto.WrappedKey wrappedKey = KeyWrapUtils.wrap(secondaryKey, generateAesKey());
-        wrappedKey.wrapAlgorithm = WrappedKeyProto.WrappedKey.UNKNOWN;
-
-        KeyWrapUtils.unwrap(secondaryKey, wrappedKey);
-    }
-
-    /** Ensure the unwrap method rejects metadata indicating the encryption type is unknown */
-    @Test(expected = InvalidKeyException.class)
-    public void unwrap_throwsForBadKeyAlgorithm() throws Exception {
-        SecretKey secondaryKey = generateAesKey();
-        WrappedKeyProto.WrappedKey wrappedKey = KeyWrapUtils.wrap(secondaryKey, generateAesKey());
-        wrappedKey.metadata.type = WrappedKeyProto.KeyMetadata.UNKNOWN;
-
-        KeyWrapUtils.unwrap(secondaryKey, wrappedKey);
-    }
-
-    /** Ensure the unwrap method rejects wrapped keys missing the metadata */
-    @Test(expected = InvalidKeyException.class)
-    public void unwrap_throwsForMissingMetadata() throws Exception {
-        SecretKey secondaryKey = generateAesKey();
-        WrappedKeyProto.WrappedKey wrappedKey = KeyWrapUtils.wrap(secondaryKey, generateAesKey());
-        wrappedKey.metadata = null;
-
-        KeyWrapUtils.unwrap(secondaryKey, wrappedKey);
-    }
-
-    /** Ensure unwrap rejects invalid secondary keys */
-    @Test(expected = InvalidKeyException.class)
-    public void unwrap_throwsForBadSecondaryKey() throws Exception {
-        WrappedKeyProto.WrappedKey wrappedKey =
-                KeyWrapUtils.wrap(
-                        /*secondaryKey=*/ generateAesKey(), /*tertiaryKey=*/ generateAesKey());
-
-        KeyWrapUtils.unwrap(generateAesKey(), wrappedKey);
-    }
-
-    /** Ensure rewrap can rewrap keys */
-    @Test
-    public void rewrap_canBeUnwrappedWithNewSecondaryKey() throws Exception {
-        SecretKey tertiaryKey = generateAesKey();
-        SecretKey oldSecondaryKey = generateAesKey();
-        SecretKey newSecondaryKey = generateAesKey();
-        WrappedKeyProto.WrappedKey wrappedWithOld = KeyWrapUtils.wrap(oldSecondaryKey, tertiaryKey);
-
-        WrappedKeyProto.WrappedKey wrappedWithNew =
-                KeyWrapUtils.rewrap(oldSecondaryKey, newSecondaryKey, wrappedWithOld);
-
-        assertThat(KeyWrapUtils.unwrap(newSecondaryKey, wrappedWithNew)).isEqualTo(tertiaryKey);
-    }
-
-    /** Ensure rewrap doesn't create something decryptable by an old key */
-    @Test(expected = InvalidKeyException.class)
-    public void rewrap_cannotBeUnwrappedWithOldSecondaryKey() throws Exception {
-        SecretKey tertiaryKey = generateAesKey();
-        SecretKey oldSecondaryKey = generateAesKey();
-        SecretKey newSecondaryKey = generateAesKey();
-        WrappedKeyProto.WrappedKey wrappedWithOld = KeyWrapUtils.wrap(oldSecondaryKey, tertiaryKey);
-
-        WrappedKeyProto.WrappedKey wrappedWithNew =
-                KeyWrapUtils.rewrap(oldSecondaryKey, newSecondaryKey, wrappedWithOld);
-
-        KeyWrapUtils.unwrap(oldSecondaryKey, wrappedWithNew);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
deleted file mode 100644
index 5342efa..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.keys;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.Context;
-import android.platform.test.annotations.Presubmit;
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.RecoveryController;
-
-import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException;
-import com.android.server.testing.shadows.ShadowRecoveryController;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-import java.security.SecureRandom;
-import java.util.Optional;
-
-/** Tests for {@link RecoverableKeyStoreSecondaryKeyManager}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class})
-public class RecoverableKeyStoreSecondaryKeyManagerTest {
-    private static final String BACKUP_KEY_ALIAS_PREFIX =
-            "com.android.server.backup/recoverablekeystore/";
-    private static final int BITS_PER_BYTE = 8;
-    private static final int BACKUP_KEY_SUFFIX_LENGTH_BYTES = 128 / BITS_PER_BYTE;
-    private static final int HEX_PER_BYTE = 2;
-    private static final int BACKUP_KEY_ALIAS_LENGTH =
-            BACKUP_KEY_ALIAS_PREFIX.length() + BACKUP_KEY_SUFFIX_LENGTH_BYTES * HEX_PER_BYTE;
-    private static final String NONEXISTENT_KEY_ALIAS = "NONEXISTENT_KEY_ALIAS";
-
-    private RecoverableKeyStoreSecondaryKeyManager mRecoverableKeyStoreSecondaryKeyManager;
-    private Context mContext;
-
-    /** Create a new {@link RecoverableKeyStoreSecondaryKeyManager} to use in tests. */
-    @Before
-    public void setUp() throws Exception {
-        mContext = RuntimeEnvironment.application;
-
-        mRecoverableKeyStoreSecondaryKeyManager =
-                new RecoverableKeyStoreSecondaryKeyManager(
-                        RecoveryController.getInstance(mContext), new SecureRandom());
-    }
-
-    /** Reset the {@link ShadowRecoveryController}. */
-    @After
-    public void tearDown() throws Exception {
-        ShadowRecoveryController.reset();
-    }
-
-    /** The generated key should always have the prefix {@code BACKUP_KEY_ALIAS_PREFIX}. */
-    @Test
-    public void generate_generatesKeyWithExpectedPrefix() throws Exception {
-        RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
-
-        assertThat(key.getAlias()).startsWith(BACKUP_KEY_ALIAS_PREFIX);
-    }
-
-    /** The generated key should always have length {@code BACKUP_KEY_ALIAS_LENGTH}. */
-    @Test
-    public void generate_generatesKeyWithExpectedLength() throws Exception {
-        RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
-
-        assertThat(key.getAlias()).hasLength(BACKUP_KEY_ALIAS_LENGTH);
-    }
-
-    /** Ensure that hidden API exceptions are rethrown when generating keys. */
-    @Test
-    public void generate_encounteringHiddenApiException_rethrowsException() {
-        ShadowRecoveryController.setThrowsInternalError(true);
-
-        assertThrows(
-                InternalRecoveryServiceException.class,
-                mRecoverableKeyStoreSecondaryKeyManager::generate);
-    }
-
-    /** Ensure that retrieved keys correspond to those generated earlier. */
-    @Test
-    public void get_getsKeyGeneratedByController() throws Exception {
-        RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
-
-        Optional<RecoverableKeyStoreSecondaryKey> retrievedKey =
-                mRecoverableKeyStoreSecondaryKeyManager.get(key.getAlias());
-
-        assertThat(retrievedKey.isPresent()).isTrue();
-        assertThat(retrievedKey.get().getAlias()).isEqualTo(key.getAlias());
-        assertThat(retrievedKey.get().getSecretKey()).isEqualTo(key.getSecretKey());
-    }
-
-    /**
-     * Ensure that a call to {@link RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)}
-     * for nonexistent aliases returns an emtpy {@link Optional}.
-     */
-    @Test
-    public void get_forNonExistentKey_returnsEmptyOptional() throws Exception {
-        Optional<RecoverableKeyStoreSecondaryKey> retrievedKey =
-                mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS);
-
-        assertThat(retrievedKey.isPresent()).isFalse();
-    }
-
-    /**
-     * Ensure that exceptions occurring during {@link
-     * RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)} are not rethrown.
-     */
-    @Test
-    public void get_encounteringInternalException_doesNotPropagateException() throws Exception {
-        ShadowRecoveryController.setThrowsInternalError(true);
-
-        // Should not throw exception
-        mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS);
-    }
-
-    /** Ensure that keys are correctly removed from the store. */
-    @Test
-    public void remove_removesKeyFromRecoverableStore() throws Exception {
-        RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
-
-        mRecoverableKeyStoreSecondaryKeyManager.remove(key.getAlias());
-
-        assertThat(RecoveryController.getInstance(mContext).getAliases())
-                .doesNotContain(key.getAlias());
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
deleted file mode 100644
index 89977f8..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.keys;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.Context;
-import android.platform.test.annotations.Presubmit;
-import android.security.keystore.recovery.RecoveryController;
-
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey.Status;
-import com.android.server.backup.testing.CryptoTestUtils;
-import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException;
-import com.android.server.testing.shadows.ShadowRecoveryController;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-import javax.crypto.SecretKey;
-
-/** Tests for {@link RecoverableKeyStoreSecondaryKey}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class})
-public class RecoverableKeyStoreSecondaryKeyTest {
-    private static final String TEST_ALIAS = "test";
-    private static final int NONEXISTENT_STATUS_CODE = 42;
-
-    private RecoverableKeyStoreSecondaryKey mSecondaryKey;
-    private SecretKey mGeneratedSecretKey;
-    private Context mContext;
-
-    /** Instantiate a {@link RecoverableKeyStoreSecondaryKey} to use in tests. */
-    @Before
-    public void setUp() throws Exception {
-        mContext = RuntimeEnvironment.application;
-        mGeneratedSecretKey = CryptoTestUtils.generateAesKey();
-        mSecondaryKey = new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, mGeneratedSecretKey);
-    }
-
-    /** Reset the {@link ShadowRecoveryController}. */
-    @After
-    public void tearDown() throws Exception {
-        ShadowRecoveryController.reset();
-    }
-
-    /**
-     * Checks that {@link RecoverableKeyStoreSecondaryKey#getAlias()} returns the value supplied in
-     * the constructor.
-     */
-    @Test
-    public void getAlias() {
-        String alias = mSecondaryKey.getAlias();
-
-        assertThat(alias).isEqualTo(TEST_ALIAS);
-    }
-
-    /**
-     * Checks that {@link RecoverableKeyStoreSecondaryKey#getSecretKey()} returns the value supplied
-     * in the constructor.
-     */
-    @Test
-    public void getSecretKey() {
-        SecretKey secretKey = mSecondaryKey.getSecretKey();
-
-        assertThat(secretKey).isEqualTo(mGeneratedSecretKey);
-    }
-
-    /**
-     * Checks that passing a secret key that is null to the constructor throws an exception.
-     */
-    @Test
-    public void constructor_withNullSecretKey_throwsNullPointerException() {
-        assertThrows(
-                NullPointerException.class,
-                () -> new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, null));
-    }
-
-    /**
-     * Checks that passing an alias that is null to the constructor throws an exception.
-     */
-    @Test
-    public void constructor_withNullAlias_throwsNullPointerException() {
-        assertThrows(
-                NullPointerException.class,
-                () -> new RecoverableKeyStoreSecondaryKey(null, mGeneratedSecretKey));
-    }
-
-    /** Checks that the synced status is returned correctly. */
-    @Test
-    public void getStatus_whenSynced_returnsSynced() throws Exception {
-        setStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-
-        int status = mSecondaryKey.getStatus(mContext);
-
-        assertThat(status).isEqualTo(Status.SYNCED);
-    }
-
-    /** Checks that the in progress sync status is returned correctly. */
-    @Test
-    public void getStatus_whenNotSynced_returnsNotSynced() throws Exception {
-        setStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
-
-        int status = mSecondaryKey.getStatus(mContext);
-
-        assertThat(status).isEqualTo(Status.NOT_SYNCED);
-    }
-
-    /** Checks that the failure status is returned correctly. */
-    @Test
-    public void getStatus_onPermanentFailure_returnsDestroyed() throws Exception {
-        setStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
-
-        int status = mSecondaryKey.getStatus(mContext);
-
-        assertThat(status).isEqualTo(Status.DESTROYED);
-    }
-
-    /** Checks that an unknown status results in {@code NOT_SYNCED} being returned. */
-    @Test
-    public void getStatus_forUnknownStatusCode_returnsNotSynced() throws Exception {
-        setStatus(NONEXISTENT_STATUS_CODE);
-
-        int status = mSecondaryKey.getStatus(mContext);
-
-        assertThat(status).isEqualTo(Status.NOT_SYNCED);
-    }
-
-    /** Checks that an internal error results in {@code NOT_SYNCED} being returned. */
-    @Test
-    public void getStatus_onInternalError_returnsNotSynced() throws Exception {
-        ShadowRecoveryController.setThrowsInternalError(true);
-
-        int status = mSecondaryKey.getStatus(mContext);
-
-        assertThat(status).isEqualTo(Status.NOT_SYNCED);
-    }
-
-    private void setStatus(int status) throws Exception {
-        ShadowRecoveryController.setRecoveryStatus(TEST_ALIAS, status);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RestoreKeyFetcherTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RestoreKeyFetcherTest.java
deleted file mode 100644
index 004f809..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RestoreKeyFetcherTest.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-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 java.security.InvalidKeyException;
-import java.security.KeyException;
-import java.security.SecureRandom;
-import java.util.Optional;
-
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-/** Test the restore key fetcher */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class RestoreKeyFetcherTest {
-
-    private static final String KEY_GENERATOR_ALGORITHM = "AES";
-
-    private static final String TEST_SECONDARY_KEY_ALIAS = "test_2ndary_key";
-    private static final byte[] TEST_SECONDARY_KEY_BYTES = new byte[256 / Byte.SIZE];
-
-    @Mock private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
-
-    /** Initialise the mocks **/
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    /** Ensure the unwrap method works as expected */
-    @Test
-    public void unwrapTertiaryKey_returnsUnwrappedKey() throws Exception {
-        RecoverableKeyStoreSecondaryKey secondaryKey = createSecondaryKey();
-        SecretKey tertiaryKey = createTertiaryKey();
-        WrappedKeyProto.WrappedKey wrappedTertiaryKey =
-                KeyWrapUtils.wrap(secondaryKey.getSecretKey(), tertiaryKey);
-        when(mSecondaryKeyManager.get(TEST_SECONDARY_KEY_ALIAS))
-                .thenReturn(Optional.of(secondaryKey));
-
-        SecretKey actualTertiaryKey =
-                RestoreKeyFetcher.unwrapTertiaryKey(
-                        () -> mSecondaryKeyManager,
-                        TEST_SECONDARY_KEY_ALIAS,
-                        wrappedTertiaryKey);
-
-        assertThat(actualTertiaryKey).isEqualTo(tertiaryKey);
-    }
-
-    /** Ensure that missing secondary keys are detected and an appropriate exception is thrown */
-    @Test
-    public void unwrapTertiaryKey_missingSecondaryKey_throwsSpecificException() throws Exception {
-        WrappedKeyProto.WrappedKey wrappedTertiaryKey =
-                KeyWrapUtils.wrap(createSecondaryKey().getSecretKey(), createTertiaryKey());
-        when(mSecondaryKeyManager.get(TEST_SECONDARY_KEY_ALIAS)).thenReturn(Optional.empty());
-
-        assertThrows(
-                KeyException.class,
-                () ->
-                        RestoreKeyFetcher.unwrapTertiaryKey(
-                                () -> mSecondaryKeyManager,
-                                TEST_SECONDARY_KEY_ALIAS,
-                                wrappedTertiaryKey));
-    }
-
-    /** Ensure that invalid secondary keys are detected and an appropriate exception is thrown */
-    @Test
-    public void unwrapTertiaryKey_badSecondaryKey_throws() throws Exception {
-        RecoverableKeyStoreSecondaryKey badSecondaryKey =
-                new RecoverableKeyStoreSecondaryKey(
-                        TEST_SECONDARY_KEY_ALIAS,
-                        new SecretKeySpec(new byte[] {0, 1}, KEY_GENERATOR_ALGORITHM));
-
-        WrappedKeyProto.WrappedKey wrappedTertiaryKey =
-                KeyWrapUtils.wrap(createSecondaryKey().getSecretKey(), createTertiaryKey());
-        when(mSecondaryKeyManager.get(TEST_SECONDARY_KEY_ALIAS))
-                .thenReturn(Optional.of(badSecondaryKey));
-
-        assertThrows(
-                InvalidKeyException.class,
-                () ->
-                        RestoreKeyFetcher.unwrapTertiaryKey(
-                                () -> mSecondaryKeyManager,
-                                TEST_SECONDARY_KEY_ALIAS,
-                                wrappedTertiaryKey));
-    }
-
-    private static RecoverableKeyStoreSecondaryKey createSecondaryKey() {
-        return new RecoverableKeyStoreSecondaryKey(
-                TEST_SECONDARY_KEY_ALIAS,
-                new SecretKeySpec(TEST_SECONDARY_KEY_BYTES, KEY_GENERATOR_ALGORITHM));
-    }
-
-    private static SecretKey createTertiaryKey() {
-        return new TertiaryKeyGenerator(new SecureRandom(new byte[] {0})).generate();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java
deleted file mode 100644
index c31d19d..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.tasks.StartSecondaryKeyRotationTask;
-
-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.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
-
-import java.io.File;
-import java.time.Clock;
-
-@Config(shadows = SecondaryKeyRotationSchedulerTest.ShadowStartSecondaryKeyRotationTask.class)
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class SecondaryKeyRotationSchedulerTest {
-    private static final String SENTINEL_FILE_PATH = "force_secondary_key_rotation";
-
-    @Mock private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
-    @Mock private Clock mClock;
-
-    private CryptoSettings mCryptoSettings;
-    private SecondaryKeyRotationScheduler mScheduler;
-    private long mRotationIntervalMillis;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        Context application = ApplicationProvider.getApplicationContext();
-
-        mCryptoSettings = CryptoSettings.getInstanceForTesting(application);
-        mRotationIntervalMillis = mCryptoSettings.backupSecondaryKeyRotationIntervalMs();
-
-        mScheduler =
-                new SecondaryKeyRotationScheduler(
-                        application, mSecondaryKeyManager, mCryptoSettings, mClock);
-        ShadowStartSecondaryKeyRotationTask.reset();
-    }
-
-    @Test
-    public void startRotationIfScheduled_rotatesIfRotationWasFarEnoughInThePast() {
-        long lastRotated = 100009;
-        mCryptoSettings.setSecondaryLastRotated(lastRotated);
-        setNow(lastRotated + mRotationIntervalMillis);
-
-        mScheduler.startRotationIfScheduled();
-
-        assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue();
-    }
-
-    @Test
-    public void startRotationIfScheduled_setsNewRotationTimeIfRotationWasFarEnoughInThePast() {
-        long lastRotated = 100009;
-        long now = lastRotated + mRotationIntervalMillis;
-        mCryptoSettings.setSecondaryLastRotated(lastRotated);
-        setNow(now);
-
-        mScheduler.startRotationIfScheduled();
-
-        assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now);
-    }
-
-    @Test
-    public void startRotationIfScheduled_rotatesIfClockHasChanged() {
-        long lastRotated = 100009;
-        mCryptoSettings.setSecondaryLastRotated(lastRotated);
-        setNow(lastRotated - 1);
-
-        mScheduler.startRotationIfScheduled();
-
-        assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue();
-    }
-
-    @Test
-    public void startRotationIfScheduled_rotatesIfSentinelFileIsPresent() throws Exception {
-        File file = new File(RuntimeEnvironment.application.getFilesDir(), SENTINEL_FILE_PATH);
-        file.createNewFile();
-
-        mScheduler.startRotationIfScheduled();
-
-        assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue();
-    }
-
-    @Test
-    public void startRotationIfScheduled_setsNextRotationIfClockHasChanged() {
-        long lastRotated = 100009;
-        long now = lastRotated - 1;
-        mCryptoSettings.setSecondaryLastRotated(lastRotated);
-        setNow(now);
-
-        mScheduler.startRotationIfScheduled();
-
-        assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now);
-    }
-
-    @Test
-    public void startRotationIfScheduled_doesNothingIfRotationWasRecentEnough() {
-        long lastRotated = 100009;
-        mCryptoSettings.setSecondaryLastRotated(lastRotated);
-        setNow(lastRotated + mRotationIntervalMillis - 1);
-
-        mScheduler.startRotationIfScheduled();
-
-        assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isFalse();
-    }
-
-    @Test
-    public void startRotationIfScheduled_doesNotSetRotationTimeIfRotationWasRecentEnough() {
-        long lastRotated = 100009;
-        mCryptoSettings.setSecondaryLastRotated(lastRotated);
-        setNow(lastRotated + mRotationIntervalMillis - 1);
-
-        mScheduler.startRotationIfScheduled();
-
-        assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(lastRotated);
-    }
-
-    @Test
-    public void startRotationIfScheduled_setsLastRotatedToNowIfNeverRotated() {
-        long now = 13295436;
-        setNow(now);
-
-        mScheduler.startRotationIfScheduled();
-
-        assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now);
-    }
-
-    private void setNow(long timestamp) {
-        when(mClock.millis()).thenReturn(timestamp);
-    }
-
-    @Implements(StartSecondaryKeyRotationTask.class)
-    public static class ShadowStartSecondaryKeyRotationTask {
-        private static boolean sRan = false;
-
-        @Implementation
-        public void run() {
-            sRan = true;
-        }
-
-        @Resetter
-        public static void reset() {
-            sRan = false;
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
deleted file mode 100644
index 48216f8..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.keys;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.security.SecureRandom;
-
-import javax.crypto.SecretKey;
-
-/** Tests for {@link TertiaryKeyGenerator}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class TertiaryKeyGeneratorTest {
-    private static final String KEY_ALGORITHM = "AES";
-    private static final int KEY_SIZE_BITS = 256;
-
-    private TertiaryKeyGenerator mTertiaryKeyGenerator;
-
-    /** Instantiate a new {@link TertiaryKeyGenerator} for use in tests. */
-    @Before
-    public void setUp() {
-        mTertiaryKeyGenerator = new TertiaryKeyGenerator(new SecureRandom());
-    }
-
-    /** Generated keys should be AES keys. */
-    @Test
-    public void generate_generatesAESKeys() {
-        SecretKey secretKey = mTertiaryKeyGenerator.generate();
-
-        assertThat(secretKey.getAlgorithm()).isEqualTo(KEY_ALGORITHM);
-    }
-
-    /** Generated keys should be 256 bits in size. */
-    @Test
-    public void generate_generates256BitKeys() {
-        SecretKey secretKey = mTertiaryKeyGenerator.generate();
-
-        assertThat(secretKey.getEncoded()).hasLength(KEY_SIZE_BITS / 8);
-    }
-
-    /**
-     * Subsequent calls to {@link TertiaryKeyGenerator#generate()} should generate different keys.
-     */
-    @Test
-    public void generate_generatesNewKeys() {
-        SecretKey key1 = mTertiaryKeyGenerator.generate();
-        SecretKey key2 = mTertiaryKeyGenerator.generate();
-
-        assertThat(key1).isNotEqualTo(key2);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyManagerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyManagerTest.java
deleted file mode 100644
index 1ed8309..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyManagerTest.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.robolectric.RuntimeEnvironment.application;
-
-import android.security.keystore.recovery.RecoveryController;
-
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-import com.android.server.testing.shadows.ShadowRecoveryController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-import java.security.SecureRandom;
-
-import javax.crypto.SecretKey;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowRecoveryController.class)
-public class TertiaryKeyManagerTest {
-
-    private static final String TEST_PACKAGE_1 = "com.example.app1";
-    private static final String TEST_PACKAGE_2 = "com.example.app2";
-
-    private SecureRandom mSecureRandom;
-    private RecoverableKeyStoreSecondaryKey mSecondaryKey;
-
-    @Mock private TertiaryKeyRotationScheduler mTertiaryKeyRotationScheduler;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mSecureRandom = new SecureRandom();
-        mSecondaryKey =
-                new RecoverableKeyStoreSecondaryKeyManager(
-                                RecoveryController.getInstance(application), mSecureRandom)
-                        .generate();
-        ShadowRecoveryController.reset();
-    }
-
-    private TertiaryKeyManager createNewManager(String packageName) {
-        return new TertiaryKeyManager(
-                application,
-                mSecureRandom,
-                mTertiaryKeyRotationScheduler,
-                mSecondaryKey,
-                packageName);
-    }
-
-    @Test
-    public void getKey_noExistingKey_returnsNewKey() throws Exception {
-        assertThat(createNewManager(TEST_PACKAGE_1).getKey()).isNotNull();
-    }
-
-    @Test
-    public void getKey_noExistingKey_recordsIncrementalBackup() throws Exception {
-        createNewManager(TEST_PACKAGE_1).getKey();
-        verify(mTertiaryKeyRotationScheduler).recordBackup(TEST_PACKAGE_1);
-    }
-
-    @Test
-    public void getKey_existingKey_returnsExistingKey() throws Exception {
-        TertiaryKeyManager manager = createNewManager(TEST_PACKAGE_1);
-        SecretKey existingKey = manager.getKey();
-
-        assertThat(manager.getKey()).isEqualTo(existingKey);
-    }
-
-    @Test
-    public void getKey_existingKey_recordsBackupButNotRotation() throws Exception {
-        createNewManager(TEST_PACKAGE_1).getKey();
-        reset(mTertiaryKeyRotationScheduler);
-
-        createNewManager(TEST_PACKAGE_1).getKey();
-
-        verify(mTertiaryKeyRotationScheduler).recordBackup(TEST_PACKAGE_1);
-        verify(mTertiaryKeyRotationScheduler, never()).recordKeyRotation(any());
-    }
-
-    @Test
-    public void getKey_existingKeyButRotationRequired_returnsNewKey() throws Exception {
-        SecretKey firstKey = createNewManager(TEST_PACKAGE_1).getKey();
-        when(mTertiaryKeyRotationScheduler.isKeyRotationDue(TEST_PACKAGE_1)).thenReturn(true);
-
-        SecretKey secondKey = createNewManager(TEST_PACKAGE_1).getKey();
-
-        assertThat(secondKey).isNotEqualTo(firstKey);
-    }
-
-    @Test
-    public void getKey_existingKeyButRotationRequired_recordsKeyRotationAndBackup()
-            throws Exception {
-        when(mTertiaryKeyRotationScheduler.isKeyRotationDue(TEST_PACKAGE_1)).thenReturn(true);
-        createNewManager(TEST_PACKAGE_1).getKey();
-
-        InOrder inOrder = inOrder(mTertiaryKeyRotationScheduler);
-        inOrder.verify(mTertiaryKeyRotationScheduler).recordKeyRotation(TEST_PACKAGE_1);
-        inOrder.verify(mTertiaryKeyRotationScheduler).recordBackup(TEST_PACKAGE_1);
-    }
-
-    @Test
-    public void getKey_twoApps_returnsDifferentKeys() throws Exception {
-        TertiaryKeyManager firstManager = createNewManager(TEST_PACKAGE_1);
-        TertiaryKeyManager secondManager = createNewManager(TEST_PACKAGE_2);
-        SecretKey firstKey = firstManager.getKey();
-
-        assertThat(secondManager.getKey()).isNotEqualTo(firstKey);
-    }
-
-    @Test
-    public void getWrappedKey_noExistingKey_returnsWrappedNewKey() throws Exception {
-        TertiaryKeyManager manager = createNewManager(TEST_PACKAGE_1);
-        SecretKey unwrappedKey = manager.getKey();
-        WrappedKeyProto.WrappedKey wrappedKey = manager.getWrappedKey();
-
-        SecretKey expectedUnwrappedKey =
-                KeyWrapUtils.unwrap(mSecondaryKey.getSecretKey(), wrappedKey);
-        assertThat(unwrappedKey).isEqualTo(expectedUnwrappedKey);
-    }
-
-    @Test
-    public void getWrappedKey_existingKey_returnsWrappedExistingKey() throws Exception {
-        TertiaryKeyManager manager = createNewManager(TEST_PACKAGE_1);
-        WrappedKeyProto.WrappedKey wrappedKey = manager.getWrappedKey();
-        SecretKey unwrappedKey = manager.getKey();
-
-        SecretKey expectedUnwrappedKey =
-                KeyWrapUtils.unwrap(mSecondaryKey.getSecretKey(), wrappedKey);
-        assertThat(unwrappedKey).isEqualTo(expectedUnwrappedKey);
-    }
-
-    @Test
-    public void wasKeyRotated_noExistingKey_returnsTrue() throws Exception {
-        TertiaryKeyManager manager = createNewManager(TEST_PACKAGE_1);
-        assertThat(manager.wasKeyRotated()).isTrue();
-    }
-
-    @Test
-    public void wasKeyRotated_existingKey_returnsFalse() throws Exception {
-        createNewManager(TEST_PACKAGE_1).getKey();
-        assertThat(createNewManager(TEST_PACKAGE_1).wasKeyRotated()).isFalse();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationSchedulerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationSchedulerTest.java
deleted file mode 100644
index dfc7e2b..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationSchedulerTest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.robolectric.RuntimeEnvironment.application;
-
-import android.content.Context;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.File;
-import java.time.Clock;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-
-/** Tests for the tertiary key rotation scheduler */
-@RunWith(RobolectricTestRunner.class)
-public final class TertiaryKeyRotationSchedulerTest {
-
-    private static final int MAXIMUM_ROTATIONS_PER_WINDOW = 2;
-    private static final int MAX_BACKUPS_TILL_ROTATION = 31;
-    private static final String SHARED_PREFS_NAME = "tertiary_key_rotation_tracker";
-    private static final String PACKAGE_1 = "com.android.example1";
-    private static final String PACKAGE_2 = "com.android.example2";
-
-    @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
-
-    @Mock private Clock mClock;
-
-    private File mFile;
-    private TertiaryKeyRotationScheduler mScheduler;
-
-    /** Setup the scheduler for test */
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mFile = temporaryFolder.newFile();
-        mScheduler =
-                new TertiaryKeyRotationScheduler(
-                        new TertiaryKeyRotationTracker(
-                                application.getSharedPreferences(
-                                        SHARED_PREFS_NAME, Context.MODE_PRIVATE),
-                                MAX_BACKUPS_TILL_ROTATION),
-                        new TertiaryKeyRotationWindowedCount(mFile, mClock),
-                        MAXIMUM_ROTATIONS_PER_WINDOW);
-    }
-
-    /** Test we don't trigger a rotation straight off */
-    @Test
-    public void isKeyRotationDue_isFalseInitially() {
-        assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isFalse();
-    }
-
-    /** Test we don't prematurely trigger a rotation */
-    @Test
-    public void isKeyRotationDue_isFalseAfterInsufficientBackups() {
-        simulateBackups(MAX_BACKUPS_TILL_ROTATION - 1);
-        assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isFalse();
-    }
-
-    /** Test we do trigger a backup */
-    @Test
-    public void isKeyRotationDue_isTrueAfterEnoughBackups() {
-        simulateBackups(MAX_BACKUPS_TILL_ROTATION);
-        assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isTrue();
-    }
-
-    /** Test rotation will occur if the quota allows */
-    @Test
-    public void isKeyRotationDue_isTrueIfRotationQuotaRemainsInWindow() {
-        simulateBackups(MAX_BACKUPS_TILL_ROTATION);
-        mScheduler.recordKeyRotation(PACKAGE_2);
-        assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isTrue();
-    }
-
-    /** Test rotation is blocked if the quota has been exhausted */
-    @Test
-    public void isKeyRotationDue_isFalseIfEnoughRotationsHaveHappenedInWindow() {
-        simulateBackups(MAX_BACKUPS_TILL_ROTATION);
-        mScheduler.recordKeyRotation(PACKAGE_2);
-        mScheduler.recordKeyRotation(PACKAGE_2);
-        assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isFalse();
-    }
-
-    /** Test rotation is due after one window has passed */
-    @Test
-    public void isKeyRotationDue_isTrueAfterAWholeWindowHasPassed() {
-        simulateBackups(MAX_BACKUPS_TILL_ROTATION);
-        mScheduler.recordKeyRotation(PACKAGE_2);
-        mScheduler.recordKeyRotation(PACKAGE_2);
-        setTimeMillis(TimeUnit.HOURS.toMillis(24));
-        assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isTrue();
-    }
-
-    /** Test the rotation state changes after a rotation */
-    @Test
-    public void isKeyRotationDue_isFalseAfterRotation() {
-        simulateBackups(MAX_BACKUPS_TILL_ROTATION);
-        mScheduler.recordKeyRotation(PACKAGE_1);
-        assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isFalse();
-    }
-
-    /** Test the rate limiting for a given window */
-    @Test
-    public void isKeyRotationDue_neverAllowsMoreThanInWindow() {
-        List<String> apps = makeTestApps(MAXIMUM_ROTATIONS_PER_WINDOW * MAX_BACKUPS_TILL_ROTATION);
-
-        // simulate backups of all apps each night
-        for (int i = 0; i < 300; i++) {
-            setTimeMillis(i * TimeUnit.HOURS.toMillis(24));
-            int rotationsThisNight = 0;
-            for (String app : apps) {
-                if (mScheduler.isKeyRotationDue(app)) {
-                    rotationsThisNight++;
-                    mScheduler.recordKeyRotation(app);
-                } else {
-                    mScheduler.recordBackup(app);
-                }
-            }
-            assertThat(rotationsThisNight).isAtMost(MAXIMUM_ROTATIONS_PER_WINDOW);
-        }
-    }
-
-    /** Test that backups are staggered over the window */
-    @Test
-    public void isKeyRotationDue_naturallyStaggersBackupsOverTime() {
-        List<String> apps = makeTestApps(MAXIMUM_ROTATIONS_PER_WINDOW * MAX_BACKUPS_TILL_ROTATION);
-
-        HashMap<String, ArrayList<Integer>> rotationDays = new HashMap<>();
-        for (String app : apps) {
-            rotationDays.put(app, new ArrayList<>());
-        }
-
-        // simulate backups of all apps each night
-        for (int i = 0; i < 300; i++) {
-            setTimeMillis(i * TimeUnit.HOURS.toMillis(24));
-            for (String app : apps) {
-                if (mScheduler.isKeyRotationDue(app)) {
-                    rotationDays.get(app).add(i);
-                    mScheduler.recordKeyRotation(app);
-                } else {
-                    mScheduler.recordBackup(app);
-                }
-            }
-        }
-
-        for (String app : apps) {
-            List<Integer> days = rotationDays.get(app);
-            for (int i = 1; i < days.size(); i++) {
-                assertThat(days.get(i) - days.get(i - 1)).isEqualTo(MAX_BACKUPS_TILL_ROTATION + 1);
-            }
-        }
-    }
-
-    private ArrayList<String> makeTestApps(int n) {
-        ArrayList<String> apps = new ArrayList<>();
-        for (int i = 0; i < n; i++) {
-            apps.add(String.format(Locale.US, "com.android.app%d", i));
-        }
-        return apps;
-    }
-
-    private void simulateBackups(int numberOfBackups) {
-        while (numberOfBackups > 0) {
-            mScheduler.recordBackup(PACKAGE_1);
-            numberOfBackups--;
-        }
-    }
-
-    private void setTimeMillis(long timeMillis) {
-        when(mClock.millis()).thenReturn(timeMillis);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
deleted file mode 100644
index 49bb410..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.keys;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-/** Tests for {@link TertiaryKeyRotationTracker}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class TertiaryKeyRotationTrackerTest {
-    private static final String PACKAGE_1 = "com.package.one";
-    private static final int NUMBER_OF_BACKUPS_BEFORE_ROTATION = 31;
-
-    private TertiaryKeyRotationTracker mTertiaryKeyRotationTracker;
-
-    /** Instantiate a {@link TertiaryKeyRotationTracker} for use in tests. */
-    @Before
-    public void setUp() {
-        mTertiaryKeyRotationTracker = newInstance();
-    }
-
-    /** New packages should not be due for key rotation. */
-    @Test
-    public void isKeyRotationDue_forNewPackage_isFalse() {
-        // Simulate a new package by not calling simulateBackups(). As a result, PACKAGE_1 hasn't
-        // been seen by mTertiaryKeyRotationTracker before.
-        boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
-
-        assertThat(keyRotationDue).isFalse();
-    }
-
-    /**
-     * Key rotation should not be due after less than {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION}
-     * backups.
-     */
-    @Test
-    public void isKeyRotationDue_afterLessThanRotationAmountBackups_isFalse() {
-        simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION - 1);
-
-        boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
-
-        assertThat(keyRotationDue).isFalse();
-    }
-
-    /** Key rotation should be due after {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION} backups. */
-    @Test
-    public void isKeyRotationDue_afterRotationAmountBackups_isTrue() {
-        simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
-
-        boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
-
-        assertThat(keyRotationDue).isTrue();
-    }
-
-    /**
-     * A call to {@link TertiaryKeyRotationTracker#resetCountdown(String)} should make sure no key
-     * rotation is due.
-     */
-    @Test
-    public void resetCountdown_makesKeyRotationNotDue() {
-        simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
-
-        mTertiaryKeyRotationTracker.resetCountdown(PACKAGE_1);
-
-        assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse();
-    }
-
-    /**
-     * New instances of {@link TertiaryKeyRotationTracker} should read state about the number of
-     * backups from disk.
-     */
-    @Test
-    public void isKeyRotationDue_forNewInstance_readsStateFromDisk() {
-        simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
-
-        boolean keyRotationDueForNewInstance = newInstance().isKeyRotationDue(PACKAGE_1);
-
-        assertThat(keyRotationDueForNewInstance).isTrue();
-    }
-
-    /**
-     * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should mark all previously
-     * seen packages for rotation.
-     */
-    @Test
-    public void markAllForRotation_marksSeenPackagesForKeyRotation() {
-        simulateBackups(PACKAGE_1, /*numberOfBackups=*/ 1);
-
-        mTertiaryKeyRotationTracker.markAllForRotation();
-
-        assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isTrue();
-    }
-
-    /**
-     * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should not mark any new
-     * packages for rotation.
-     */
-    @Test
-    public void markAllForRotation_doesNotMarkUnseenPackages() {
-        mTertiaryKeyRotationTracker.markAllForRotation();
-
-        assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse();
-    }
-
-    private void simulateBackups(String packageName, int numberOfBackups) {
-        while (numberOfBackups > 0) {
-            mTertiaryKeyRotationTracker.recordBackup(packageName);
-            numberOfBackups--;
-        }
-    }
-
-    private static TertiaryKeyRotationTracker newInstance() {
-        return TertiaryKeyRotationTracker.getInstance(RuntimeEnvironment.application);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCountTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCountTest.java
deleted file mode 100644
index bd30977..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCountTest.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.File;
-import java.io.IOException;
-import java.time.Clock;
-import java.util.concurrent.TimeUnit;
-
-/** Tests for {@link TertiaryKeyRotationWindowedCount}. */
-@RunWith(RobolectricTestRunner.class)
-public class TertiaryKeyRotationWindowedCountTest {
-    private static final int TIMESTAMP_SIZE_IN_BYTES = 8;
-
-    @Rule public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
-    @Mock private Clock mClock;
-
-    private File mFile;
-    private TertiaryKeyRotationWindowedCount mWindowedcount;
-
-    /** Setup the windowed counter for testing */
-    @Before
-    public void setUp() throws IOException {
-        MockitoAnnotations.initMocks(this);
-        mFile = mTemporaryFolder.newFile();
-        mWindowedcount = new TertiaryKeyRotationWindowedCount(mFile, mClock);
-    }
-
-    /** Test handling bad files */
-    @Test
-    public void constructor_doesNotFailForBadFile() throws IOException {
-        new TertiaryKeyRotationWindowedCount(mTemporaryFolder.newFolder(), mClock);
-    }
-
-    /** Test the count is 0 to start */
-    @Test
-    public void getCount_isZeroInitially() {
-        assertThat(mWindowedcount.getCount()).isEqualTo(0);
-    }
-
-    /** Test the count is correct for a time window */
-    @Test
-    public void getCount_includesResultsInLastTwentyFourHours() {
-        setTimeMillis(0);
-        mWindowedcount.record();
-        setTimeMillis(TimeUnit.HOURS.toMillis(4));
-        mWindowedcount.record();
-        setTimeMillis(TimeUnit.HOURS.toMillis(23));
-        mWindowedcount.record();
-        mWindowedcount.record();
-        assertThat(mWindowedcount.getCount()).isEqualTo(4);
-    }
-
-    /** Test old results are ignored */
-    @Test
-    public void getCount_ignoresResultsOlderThanTwentyFourHours() {
-        setTimeMillis(0);
-        mWindowedcount.record();
-        setTimeMillis(TimeUnit.HOURS.toMillis(24));
-        assertThat(mWindowedcount.getCount()).isEqualTo(0);
-    }
-
-    /** Test future events are removed if the clock moves backways (e.g. DST, TZ change) */
-    @Test
-    public void getCount_removesFutureEventsIfClockHasChanged() {
-        setTimeMillis(1000);
-        mWindowedcount.record();
-        setTimeMillis(0);
-        assertThat(mWindowedcount.getCount()).isEqualTo(0);
-    }
-
-    /** Check recording doesn't fail for a bad file */
-    @Test
-    public void record_doesNotFailForBadFile() throws Exception {
-        new TertiaryKeyRotationWindowedCount(mTemporaryFolder.newFolder(), mClock).record();
-    }
-
-    /** Checks the state is persisted */
-    @Test
-    public void record_persistsStateToDisk() {
-        setTimeMillis(0);
-        mWindowedcount.record();
-        assertThat(new TertiaryKeyRotationWindowedCount(mFile, mClock).getCount()).isEqualTo(1);
-    }
-
-    /** Test the file doesn't contain unnecessary data */
-    @Test
-    public void record_compactsFileToLast24Hours() {
-        setTimeMillis(0);
-        mWindowedcount.record();
-        assertThat(mFile.length()).isEqualTo(TIMESTAMP_SIZE_IN_BYTES);
-        setTimeMillis(1);
-        mWindowedcount.record();
-        assertThat(mFile.length()).isEqualTo(2 * TIMESTAMP_SIZE_IN_BYTES);
-        setTimeMillis(TimeUnit.HOURS.toMillis(24));
-        mWindowedcount.record();
-        assertThat(mFile.length()).isEqualTo(2 * TIMESTAMP_SIZE_IN_BYTES);
-    }
-
-    private void setTimeMillis(long timeMillis) {
-        when(mClock.millis()).thenReturn(timeMillis);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyStoreTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyStoreTest.java
deleted file mode 100644
index ccc5f32..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyStoreTest.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * 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.encryption.keys;
-
-import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.assertTrue;
-
-import android.content.Context;
-
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.security.InvalidKeyException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-
-import javax.crypto.SecretKey;
-
-/** Tests for the tertiary key store */
-@RunWith(RobolectricTestRunner.class)
-public class TertiaryKeyStoreTest {
-
-    private static final String SECONDARY_KEY_ALIAS = "Robbo/Ranx";
-
-    private Context mApplication;
-    private TertiaryKeyStore mTertiaryKeyStore;
-    private SecretKey mSecretKey;
-
-    /** Initialise the keystore for testing */
-    @Before
-    public void setUp() throws Exception {
-        mApplication = RuntimeEnvironment.application;
-        mSecretKey = generateAesKey();
-        mTertiaryKeyStore =
-                TertiaryKeyStore.newInstance(
-                        mApplication,
-                        new RecoverableKeyStoreSecondaryKey(SECONDARY_KEY_ALIAS, mSecretKey));
-    }
-
-    /** Test a reound trip for a key */
-    @Test
-    public void load_loadsAKeyThatWasSaved() throws Exception {
-        String packageName = "com.android.example";
-        SecretKey packageKey = generateAesKey();
-        mTertiaryKeyStore.save(packageName, packageKey);
-
-        Optional<SecretKey> maybeLoadedKey = mTertiaryKeyStore.load(packageName);
-
-        assertTrue(maybeLoadedKey.isPresent());
-        assertEquals(packageKey, maybeLoadedKey.get());
-    }
-
-    /** Test isolation between packages */
-    @Test
-    public void load_doesNotLoadAKeyForAnotherSecondary() throws Exception {
-        String packageName = "com.android.example";
-        SecretKey packageKey = generateAesKey();
-        mTertiaryKeyStore.save(packageName, packageKey);
-        TertiaryKeyStore managerWithOtherSecondaryKey =
-                TertiaryKeyStore.newInstance(
-                        mApplication,
-                        new RecoverableKeyStoreSecondaryKey(
-                                "myNewSecondaryKeyAlias", generateAesKey()));
-
-        assertFalse(managerWithOtherSecondaryKey.load(packageName).isPresent());
-    }
-
-    /** Test non-existent key handling */
-    @Test
-    public void load_returnsAbsentForANonExistentKey() throws Exception {
-        assertFalse(mTertiaryKeyStore.load("mystery.package").isPresent());
-    }
-
-    /** Test handling incorrect keys */
-    @Test
-    public void load_throwsIfHasWrongBackupKey() throws Exception {
-        String packageName = "com.android.example";
-        SecretKey packageKey = generateAesKey();
-        mTertiaryKeyStore.save(packageName, packageKey);
-        TertiaryKeyStore managerWithBadKey =
-                TertiaryKeyStore.newInstance(
-                        mApplication,
-                        new RecoverableKeyStoreSecondaryKey(SECONDARY_KEY_ALIAS, generateAesKey()));
-
-        assertThrows(InvalidKeyException.class, () -> managerWithBadKey.load(packageName));
-    }
-
-    /** Test handling of empty app name */
-    @Test
-    public void load_throwsForEmptyApplicationName() throws Exception {
-        assertThrows(IllegalArgumentException.class, () -> mTertiaryKeyStore.load(""));
-    }
-
-    /** Test handling of an invalid app name */
-    @Test
-    public void load_throwsForBadApplicationName() throws Exception {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> mTertiaryKeyStore.load("com/android/example"));
-    }
-
-    /** Test key replacement */
-    @Test
-    public void save_overwritesPreviousKey() throws Exception {
-        String packageName = "com.android.example";
-        SecretKey oldKey = generateAesKey();
-        mTertiaryKeyStore.save(packageName, oldKey);
-        SecretKey newKey = generateAesKey();
-
-        mTertiaryKeyStore.save(packageName, newKey);
-
-        Optional<SecretKey> maybeLoadedKey = mTertiaryKeyStore.load(packageName);
-        assertTrue(maybeLoadedKey.isPresent());
-        SecretKey loadedKey = maybeLoadedKey.get();
-        assertThat(loadedKey).isNotEqualTo(oldKey);
-        assertThat(loadedKey).isEqualTo(newKey);
-    }
-
-    /** Test saving with an empty application name fails */
-    @Test
-    public void save_throwsForEmptyApplicationName() throws Exception {
-        assertThrows(
-                IllegalArgumentException.class, () -> mTertiaryKeyStore.save("", generateAesKey()));
-    }
-
-    /** Test saving an invalid application name fails */
-    @Test
-    public void save_throwsForBadApplicationName() throws Exception {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> mTertiaryKeyStore.save("com/android/example", generateAesKey()));
-    }
-
-    /** Test handling an empty database */
-    @Test
-    public void getAll_returnsEmptyMapForEmptyDb() throws Exception {
-        assertThat(mTertiaryKeyStore.getAll()).isEmpty();
-    }
-
-    /** Test loading all available keys works as expected */
-    @Test
-    public void getAll_returnsAllKeysSaved() throws Exception {
-        String package1 = "com.android.example";
-        SecretKey key1 = generateAesKey();
-        String package2 = "com.anndroid.example1";
-        SecretKey key2 = generateAesKey();
-        String package3 = "com.android.example2";
-        SecretKey key3 = generateAesKey();
-        mTertiaryKeyStore.save(package1, key1);
-        mTertiaryKeyStore.save(package2, key2);
-        mTertiaryKeyStore.save(package3, key3);
-
-        Map<String, SecretKey> keys = mTertiaryKeyStore.getAll();
-
-        assertThat(keys).containsExactly(package1, key1, package2, key2, package3, key3);
-    }
-
-    /** Test cross-secondary isolation */
-    @Test
-    public void getAll_doesNotReturnKeysForOtherSecondary() throws Exception {
-        String packageName = "com.android.example";
-        TertiaryKeyStore managerWithOtherSecondaryKey =
-                TertiaryKeyStore.newInstance(
-                        mApplication,
-                        new RecoverableKeyStoreSecondaryKey(
-                                "myNewSecondaryKeyAlias", generateAesKey()));
-        managerWithOtherSecondaryKey.save(packageName, generateAesKey());
-
-        assertThat(mTertiaryKeyStore.getAll()).isEmpty();
-    }
-
-    /** Test mass put into the keystore */
-    @Test
-    public void putAll_putsAllWrappedKeysInTheStore() throws Exception {
-        String packageName = "com.android.example";
-        SecretKey key = generateAesKey();
-        WrappedKeyProto.WrappedKey wrappedKey = KeyWrapUtils.wrap(mSecretKey, key);
-
-        Map<String, WrappedKeyProto.WrappedKey> testElements = new HashMap<>();
-        testElements.put(packageName, wrappedKey);
-        mTertiaryKeyStore.putAll(testElements);
-
-        assertThat(mTertiaryKeyStore.getAll()).containsKey(packageName);
-        assertThat(mTertiaryKeyStore.getAll().get(packageName).getEncoded())
-                .isEqualTo(key.getEncoded());
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java
deleted file mode 100644
index 215e1cb..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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.encryption.kv;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-
-import android.os.Debug;
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.ChunkHasher;
-import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
-
-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 java.security.MessageDigest;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.stream.Stream;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class DecryptedChunkKvOutputTest {
-    private static final String TEST_KEY_1 = "key_1";
-    private static final String TEST_KEY_2 = "key_2";
-    private static final byte[] TEST_VALUE_1 = {1, 2, 3};
-    private static final byte[] TEST_VALUE_2 = {10, 11, 12, 13};
-    private static final byte[] TEST_PAIR_1 = toByteArray(createPair(TEST_KEY_1, TEST_VALUE_1));
-    private static final byte[] TEST_PAIR_2 = toByteArray(createPair(TEST_KEY_2, TEST_VALUE_2));
-    private static final int TEST_BUFFER_SIZE = Math.max(TEST_PAIR_1.length, TEST_PAIR_2.length);
-
-    @Mock private ChunkHasher mChunkHasher;
-    private DecryptedChunkKvOutput mOutput;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        when(mChunkHasher.computeHash(any()))
-                .thenAnswer(invocation -> fakeHash(invocation.getArgument(0)));
-        mOutput = new DecryptedChunkKvOutput(mChunkHasher);
-    }
-
-    @Test
-    public void open_returnsInstance() throws Exception {
-        assertThat(mOutput.open()).isEqualTo(mOutput);
-    }
-
-    @Test
-    public void processChunk_alreadyClosed_throws() throws Exception {
-        mOutput.open();
-        mOutput.close();
-
-        assertThrows(
-                IllegalStateException.class,
-                () -> mOutput.processChunk(TEST_PAIR_1, TEST_PAIR_1.length));
-    }
-
-    @Test
-    public void getDigest_beforeClose_throws() throws Exception {
-        // TODO: b/141356823 We should add a test which calls .open() here
-        assertThrows(IllegalStateException.class, () -> mOutput.getDigest());
-    }
-
-    @Test
-    public void getDigest_returnsDigestOfSortedHashes() throws Exception {
-        mOutput.open();
-        Debug.waitForDebugger();
-        mOutput.processChunk(Arrays.copyOf(TEST_PAIR_1, TEST_BUFFER_SIZE), TEST_PAIR_1.length);
-        mOutput.processChunk(Arrays.copyOf(TEST_PAIR_2, TEST_BUFFER_SIZE), TEST_PAIR_2.length);
-        mOutput.close();
-
-        byte[] actualDigest = mOutput.getDigest();
-
-        MessageDigest digest = MessageDigest.getInstance(DecryptedChunkKvOutput.DIGEST_ALGORITHM);
-        Stream.of(TEST_PAIR_1, TEST_PAIR_2)
-                .map(DecryptedChunkKvOutputTest::fakeHash)
-                .sorted(Comparator.naturalOrder())
-                .forEachOrdered(hash -> digest.update(hash.getHash()));
-        assertThat(actualDigest).isEqualTo(digest.digest());
-    }
-
-    @Test
-    public void getPairs_beforeClose_throws() throws Exception {
-        // TODO: b/141356823 We should add a test which calls .open() here
-        assertThrows(IllegalStateException.class, () -> mOutput.getPairs());
-    }
-
-    @Test
-    public void getPairs_returnsPairsSortedByKey() throws Exception {
-        mOutput.open();
-        // Write out of order to check that it sorts the chunks.
-        mOutput.processChunk(Arrays.copyOf(TEST_PAIR_2, TEST_BUFFER_SIZE), TEST_PAIR_2.length);
-        mOutput.processChunk(Arrays.copyOf(TEST_PAIR_1, TEST_BUFFER_SIZE), TEST_PAIR_1.length);
-        mOutput.close();
-
-        List<KeyValuePairProto.KeyValuePair> pairs = mOutput.getPairs();
-
-        assertThat(
-                        isInOrder(
-                                pairs,
-                                Comparator.comparing(
-                                        (KeyValuePairProto.KeyValuePair pair) -> pair.key)))
-                .isTrue();
-        assertThat(pairs).hasSize(2);
-        assertThat(pairs.get(0).key).isEqualTo(TEST_KEY_1);
-        assertThat(pairs.get(0).value).isEqualTo(TEST_VALUE_1);
-        assertThat(pairs.get(1).key).isEqualTo(TEST_KEY_2);
-        assertThat(pairs.get(1).value).isEqualTo(TEST_VALUE_2);
-    }
-
-    private static KeyValuePairProto.KeyValuePair createPair(String key, byte[] value) {
-        KeyValuePairProto.KeyValuePair pair = new KeyValuePairProto.KeyValuePair();
-        pair.key = key;
-        pair.value = value;
-        return pair;
-    }
-
-    private boolean isInOrder(
-            List<KeyValuePairProto.KeyValuePair> list,
-            Comparator<KeyValuePairProto.KeyValuePair> comparator) {
-        if (list.size() < 2) {
-            return true;
-        }
-
-        List<KeyValuePairProto.KeyValuePair> sortedList = new ArrayList<>(list);
-        Collections.sort(sortedList, comparator);
-        return list.equals(sortedList);
-    }
-
-    private static byte[] toByteArray(KeyValuePairProto.KeyValuePair nano) {
-        return KeyValuePairProto.KeyValuePair.toByteArray(nano);
-    }
-
-    private static ChunkHash fakeHash(byte[] data) {
-        return new ChunkHash(Arrays.copyOf(data, ChunkHash.HASH_LENGTH_BYTES));
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java
deleted file mode 100644
index acc6628..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.encryption.kv;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
-
-import com.google.common.collect.ImmutableMap;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.util.Arrays;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class KeyValueListingBuilderTest {
-    private static final String TEST_KEY_1 = "test_key_1";
-    private static final String TEST_KEY_2 = "test_key_2";
-    private static final ChunkHash TEST_HASH_1 =
-            new ChunkHash(Arrays.copyOf(new byte[] {1, 2}, ChunkHash.HASH_LENGTH_BYTES));
-    private static final ChunkHash TEST_HASH_2 =
-            new ChunkHash(Arrays.copyOf(new byte[] {5, 6}, ChunkHash.HASH_LENGTH_BYTES));
-
-    private KeyValueListingBuilder mBuilder;
-
-    @Before
-    public void setUp() {
-        mBuilder = new KeyValueListingBuilder();
-    }
-
-    @Test
-    public void addPair_nullKey_throws() {
-        assertThrows(NullPointerException.class, () -> mBuilder.addPair(null, TEST_HASH_1));
-    }
-
-    @Test
-    public void addPair_emptyKey_throws() {
-        assertThrows(IllegalArgumentException.class, () -> mBuilder.addPair("", TEST_HASH_1));
-    }
-
-    @Test
-    public void addPair_nullHash_throws() {
-        assertThrows(NullPointerException.class, () -> mBuilder.addPair(TEST_KEY_1, null));
-    }
-
-    @Test
-    public void build_noPairs_buildsEmptyListing() {
-        KeyValueListingProto.KeyValueListing listing = mBuilder.build();
-
-        assertThat(listing.entries).isEmpty();
-    }
-
-    @Test
-    public void build_returnsCorrectListing() {
-        mBuilder.addPair(TEST_KEY_1, TEST_HASH_1);
-
-        KeyValueListingProto.KeyValueListing listing = mBuilder.build();
-
-        assertThat(listing.entries.length).isEqualTo(1);
-        assertThat(listing.entries[0].key).isEqualTo(TEST_KEY_1);
-        assertThat(listing.entries[0].hash).isEqualTo(TEST_HASH_1.getHash());
-    }
-
-    @Test
-    public void addAll_addsAllPairsInMap() {
-        ImmutableMap<String, ChunkHash> pairs =
-                new ImmutableMap.Builder<String, ChunkHash>()
-                        .put(TEST_KEY_1, TEST_HASH_1)
-                        .put(TEST_KEY_2, TEST_HASH_2)
-                        .build();
-
-        mBuilder.addAll(pairs);
-        KeyValueListingProto.KeyValueListing listing = mBuilder.build();
-
-        assertThat(listing.entries.length).isEqualTo(2);
-        assertThat(listing.entries[0].key).isEqualTo(TEST_KEY_1);
-        assertThat(listing.entries[0].hash).isEqualTo(TEST_HASH_1.getHash());
-        assertThat(listing.entries[1].key).isEqualTo(TEST_KEY_2);
-        assertThat(listing.entries[1].hash).isEqualTo(TEST_HASH_2.getHash());
-    }
-
-    @Test
-    public void emptyListing_returnsListingWithoutAnyPairs() {
-        KeyValueListingProto.KeyValueListing emptyListing = KeyValueListingBuilder.emptyListing();
-        assertThat(emptyListing.entries).isEmpty();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
deleted file mode 100644
index 87f21bf..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.storage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-/** Tests for {@link BackupEncryptionDb}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class BackupEncryptionDbTest {
-    private BackupEncryptionDb mBackupEncryptionDb;
-
-    /** Creates an empty {@link BackupEncryptionDb} */
-    @Before
-    public void setUp() {
-        mBackupEncryptionDb = BackupEncryptionDb.newInstance(RuntimeEnvironment.application);
-    }
-
-    /**
-     * Tests that the tertiary keys table gets cleared when calling {@link
-     * BackupEncryptionDb#clear()}.
-     */
-    @Test
-    public void clear_withNonEmptyTertiaryKeysTable_clearsTertiaryKeysTable() throws Exception {
-        String secondaryKeyAlias = "secondaryKeyAlias";
-        TertiaryKeysTable tertiaryKeysTable = mBackupEncryptionDb.getTertiaryKeysTable();
-        tertiaryKeysTable.addKey(new TertiaryKey(secondaryKeyAlias, "packageName", new byte[0]));
-
-        mBackupEncryptionDb.clear();
-
-        assertThat(tertiaryKeysTable.getAllKeys(secondaryKeyAlias)).isEmpty();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
deleted file mode 100644
index 319ec89..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2018 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.encryption.storage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.testing.CryptoTestUtils;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.Map;
-import java.util.Optional;
-
-/** Tests for {@link TertiaryKeysTable}. */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class TertiaryKeysTableTest {
-    private static final int KEY_SIZE_BYTES = 32;
-    private static final String SECONDARY_ALIAS = "phoebe";
-    private static final String PACKAGE_NAME = "generic.package.name";
-
-    private TertiaryKeysTable mTertiaryKeysTable;
-
-    /** Creates an empty {@link BackupEncryptionDb}. */
-    @Before
-    public void setUp() {
-        mTertiaryKeysTable =
-                BackupEncryptionDb.newInstance(RuntimeEnvironment.application)
-                        .getTertiaryKeysTable();
-    }
-
-    /** Tests that new {@link TertiaryKey}s get successfully added to the database. */
-    @Test
-    public void addKey_onEmptyDatabase_putsKeyInDb() throws Exception {
-        byte[] key = generateRandomKey();
-        TertiaryKey keyToInsert = new TertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME, key);
-
-        long result = mTertiaryKeysTable.addKey(keyToInsert);
-
-        assertThat(result).isNotEqualTo(-1);
-        Optional<TertiaryKey> maybeKeyInDb =
-                mTertiaryKeysTable.getKey(SECONDARY_ALIAS, PACKAGE_NAME);
-        assertThat(maybeKeyInDb.isPresent()).isTrue();
-        TertiaryKey keyInDb = maybeKeyInDb.get();
-        assertTertiaryKeysEqual(keyInDb, keyToInsert);
-    }
-
-    /** Tests that keys replace older keys with the same secondary alias and package name. */
-    @Test
-    public void addKey_havingSameSecondaryAliasAndPackageName_replacesOldKey() throws Exception {
-        mTertiaryKeysTable.addKey(
-                new TertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME, generateRandomKey()));
-        byte[] newKey = generateRandomKey();
-
-        long result =
-                mTertiaryKeysTable.addKey(new TertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME, newKey));
-
-        assertThat(result).isNotEqualTo(-1);
-        TertiaryKey keyInDb = mTertiaryKeysTable.getKey(SECONDARY_ALIAS, PACKAGE_NAME).get();
-        assertThat(keyInDb.getWrappedKeyBytes()).isEqualTo(newKey);
-    }
-
-    /**
-     * Tests that keys do not replace older keys with the same package name but a different alias.
-     */
-    @Test
-    public void addKey_havingSamePackageNameButDifferentAlias_doesNotReplaceOldKey()
-            throws Exception {
-        String alias2 = "karl";
-        TertiaryKey key1 = generateTertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME);
-        TertiaryKey key2 = generateTertiaryKey(alias2, PACKAGE_NAME);
-
-        long primaryKey1 = mTertiaryKeysTable.addKey(key1);
-        long primaryKey2 = mTertiaryKeysTable.addKey(key2);
-
-        assertThat(primaryKey1).isNotEqualTo(primaryKey2);
-        assertThat(mTertiaryKeysTable.getKey(SECONDARY_ALIAS, PACKAGE_NAME).isPresent()).isTrue();
-        assertTertiaryKeysEqual(
-                mTertiaryKeysTable.getKey(SECONDARY_ALIAS, PACKAGE_NAME).get(), key1);
-        assertThat(mTertiaryKeysTable.getKey(alias2, PACKAGE_NAME).isPresent()).isTrue();
-        assertTertiaryKeysEqual(mTertiaryKeysTable.getKey(alias2, PACKAGE_NAME).get(), key2);
-    }
-
-    /**
-     * Tests that {@link TertiaryKeysTable#getKey(String, String)} returns an empty {@link Optional}
-     * for a missing key.
-     */
-    @Test
-    public void getKey_forMissingKey_returnsEmptyOptional() throws Exception {
-        Optional<TertiaryKey> key = mTertiaryKeysTable.getKey(SECONDARY_ALIAS, PACKAGE_NAME);
-
-        assertThat(key.isPresent()).isFalse();
-    }
-
-    /**
-     * Tests that {@link TertiaryKeysTable#getAllKeys(String)} returns an empty map when no keys
-     * with the secondary alias exist.
-     */
-    @Test
-    public void getAllKeys_withNoKeysForAlias_returnsEmptyMap() throws Exception {
-        assertThat(mTertiaryKeysTable.getAllKeys(SECONDARY_ALIAS)).isEmpty();
-    }
-
-    /**
-     * Tests that {@link TertiaryKeysTable#getAllKeys(String)} returns all keys corresponding to the
-     * provided secondary alias.
-     */
-    @Test
-    public void getAllKeys_withMatchingKeys_returnsAllKeysWrappedWithSecondary() throws Exception {
-        TertiaryKey key1 = generateTertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME);
-        mTertiaryKeysTable.addKey(key1);
-        String package2 = "generic.package.two";
-        TertiaryKey key2 = generateTertiaryKey(SECONDARY_ALIAS, package2);
-        mTertiaryKeysTable.addKey(key2);
-        String package3 = "generic.package.three";
-        TertiaryKey key3 = generateTertiaryKey(SECONDARY_ALIAS, package3);
-        mTertiaryKeysTable.addKey(key3);
-
-        Map<String, TertiaryKey> keysByPackageName = mTertiaryKeysTable.getAllKeys(SECONDARY_ALIAS);
-
-        assertThat(keysByPackageName).hasSize(3);
-        assertThat(keysByPackageName).containsKey(PACKAGE_NAME);
-        assertTertiaryKeysEqual(keysByPackageName.get(PACKAGE_NAME), key1);
-        assertThat(keysByPackageName).containsKey(package2);
-        assertTertiaryKeysEqual(keysByPackageName.get(package2), key2);
-        assertThat(keysByPackageName).containsKey(package3);
-        assertTertiaryKeysEqual(keysByPackageName.get(package3), key3);
-    }
-
-    /**
-     * Tests that {@link TertiaryKeysTable#getAllKeys(String)} does not return any keys wrapped with
-     * another alias.
-     */
-    @Test
-    public void getAllKeys_withMatchingKeys_doesNotReturnKeysWrappedWithOtherAlias()
-            throws Exception {
-        mTertiaryKeysTable.addKey(generateTertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME));
-        mTertiaryKeysTable.addKey(generateTertiaryKey("somekey", "generic.package.two"));
-
-        Map<String, TertiaryKey> keysByPackageName = mTertiaryKeysTable.getAllKeys(SECONDARY_ALIAS);
-
-        assertThat(keysByPackageName).hasSize(1);
-        assertThat(keysByPackageName).containsKey(PACKAGE_NAME);
-    }
-
-    private void assertTertiaryKeysEqual(TertiaryKey a, TertiaryKey b) {
-        assertThat(a.getSecondaryKeyAlias()).isEqualTo(b.getSecondaryKeyAlias());
-        assertThat(a.getPackageName()).isEqualTo(b.getPackageName());
-        assertThat(a.getWrappedKeyBytes()).isEqualTo(b.getWrappedKeyBytes());
-    }
-
-    private TertiaryKey generateTertiaryKey(String alias, String packageName) {
-        return new TertiaryKey(alias, packageName, generateRandomKey());
-    }
-
-    private byte[] generateRandomKey() {
-        return CryptoTestUtils.generateRandomBytes(KEY_SIZE_BYTES);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTaskTest.java
deleted file mode 100644
index 07a6fd2..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTaskTest.java
+++ /dev/null
@@ -1,583 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
-import static com.android.server.backup.testing.CryptoTestUtils.newChunkOrdering;
-import static com.android.server.backup.testing.CryptoTestUtils.newChunksMetadata;
-import static com.android.server.backup.testing.CryptoTestUtils.newPair;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.expectThrows;
-
-import android.annotation.Nullable;
-import android.app.backup.BackupDataInput;
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.ChunkHasher;
-import com.android.server.backup.encryption.chunking.DecryptedChunkFileOutput;
-import com.android.server.backup.encryption.chunking.EncryptedChunk;
-import com.android.server.backup.encryption.chunking.cdc.FingerprintMixer;
-import com.android.server.backup.encryption.kv.DecryptedChunkKvOutput;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkOrdering;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunksMetadata;
-import com.android.server.backup.encryption.protos.nano.KeyValuePairProto.KeyValuePair;
-import com.android.server.backup.encryption.tasks.BackupEncrypter.Result;
-import com.android.server.backup.testing.CryptoTestUtils;
-import com.android.server.testing.shadows.ShadowBackupDataInput;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.protobuf.nano.MessageNano;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.RandomAccessFile;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-import javax.crypto.AEADBadTagException;
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
-
-@Config(shadows = {ShadowBackupDataInput.class})
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class BackupFileDecryptorTaskTest {
-    private static final String READ_WRITE_MODE = "rw";
-    private static final int BYTES_PER_KILOBYTE = 1024;
-    private static final int MIN_CHUNK_SIZE_BYTES = 2 * BYTES_PER_KILOBYTE;
-    private static final int AVERAGE_CHUNK_SIZE_BYTES = 4 * BYTES_PER_KILOBYTE;
-    private static final int MAX_CHUNK_SIZE_BYTES = 64 * BYTES_PER_KILOBYTE;
-    private static final int BACKUP_DATA_SIZE_BYTES = 60 * BYTES_PER_KILOBYTE;
-    private static final int GCM_NONCE_LENGTH_BYTES = 12;
-    private static final int GCM_TAG_LENGTH_BYTES = 16;
-    private static final int BITS_PER_BYTE = 8;
-    private static final int CHECKSUM_LENGTH_BYTES = 256 / BITS_PER_BYTE;
-    @Nullable private static final FileDescriptor NULL_FILE_DESCRIPTOR = null;
-
-    private static final Set<KeyValuePair> TEST_KV_DATA = new HashSet<>();
-
-    static {
-        TEST_KV_DATA.add(newPair("key1", "value1"));
-        TEST_KV_DATA.add(newPair("key2", "value2"));
-    }
-
-    @Rule public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
-    private SecretKey mTertiaryKey;
-    private SecretKey mChunkEncryptionKey;
-    private File mInputFile;
-    private File mOutputFile;
-    private DecryptedChunkOutput mFileOutput;
-    private DecryptedChunkKvOutput mKvOutput;
-    private Random mRandom;
-    private BackupFileDecryptorTask mTask;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mRandom = new Random();
-        mTertiaryKey = generateAesKey();
-        // In good situations it's always the same. We allow changing it for testing when somehow it
-        // has become mismatched that we throw an error.
-        mChunkEncryptionKey = mTertiaryKey;
-        mInputFile = mTemporaryFolder.newFile();
-        mOutputFile = mTemporaryFolder.newFile();
-        mFileOutput = new DecryptedChunkFileOutput(mOutputFile);
-        mKvOutput = new DecryptedChunkKvOutput(new ChunkHasher(mTertiaryKey));
-        mTask = new BackupFileDecryptorTask(mTertiaryKey);
-    }
-
-    @Test
-    public void decryptFile_throwsForNonExistentInput() throws Exception {
-        assertThrows(
-                FileNotFoundException.class,
-                () ->
-                        mTask.decryptFile(
-                                new File(mTemporaryFolder.newFolder(), "nonexistent"),
-                                mFileOutput));
-    }
-
-    @Test
-    public void decryptFile_throwsForDirectoryInputFile() throws Exception {
-        assertThrows(
-                FileNotFoundException.class,
-                () -> mTask.decryptFile(mTemporaryFolder.newFolder(), mFileOutput));
-    }
-
-    @Test
-    public void decryptFile_withExplicitStarts_decryptsEncryptedData() throws Exception {
-        byte[] backupData = randomData(BACKUP_DATA_SIZE_BYTES);
-        createEncryptedFileUsingExplicitStarts(backupData);
-
-        mTask.decryptFile(mInputFile, mFileOutput);
-
-        assertThat(Files.readAllBytes(Paths.get(mOutputFile.toURI()))).isEqualTo(backupData);
-    }
-
-    @Test
-    public void decryptFile_withInlineLengths_decryptsEncryptedData() throws Exception {
-        createEncryptedFileUsingInlineLengths(
-                TEST_KV_DATA, chunkOrdering -> chunkOrdering, chunksMetadata -> chunksMetadata);
-        mTask.decryptFile(mInputFile, mKvOutput);
-        assertThat(asMap(mKvOutput.getPairs())).containsExactlyEntriesIn(asMap(TEST_KV_DATA));
-    }
-
-    @Test
-    public void decryptFile_withNoChunkOrderingType_decryptsUsingExplicitStarts() throws Exception {
-        byte[] backupData = randomData(BACKUP_DATA_SIZE_BYTES);
-        createEncryptedFileUsingExplicitStarts(
-                backupData,
-                chunkOrdering -> chunkOrdering,
-                chunksMetadata -> {
-                    ChunksMetadata metadata = CryptoTestUtils.clone(chunksMetadata);
-                    metadata.chunkOrderingType =
-                            ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
-                    return metadata;
-                });
-
-        mTask.decryptFile(mInputFile, mFileOutput);
-
-        assertThat(Files.readAllBytes(Paths.get(mOutputFile.toURI()))).isEqualTo(backupData);
-    }
-
-    @Test
-    public void decryptFile_withInlineLengths_throwsForZeroLengths() throws Exception {
-        createEncryptedFileUsingInlineLengths(
-                TEST_KV_DATA, chunkOrdering -> chunkOrdering, chunksMetadata -> chunksMetadata);
-
-        // Set the length of the first chunk to zero.
-        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
-        raf.seek(0);
-        raf.writeInt(0);
-
-        assertThrows(
-                MalformedEncryptedFileException.class,
-                () -> mTask.decryptFile(mInputFile, mKvOutput));
-    }
-
-    @Test
-    public void decryptFile_withInlineLengths_throwsForLongLengths() throws Exception {
-        createEncryptedFileUsingInlineLengths(
-                TEST_KV_DATA, chunkOrdering -> chunkOrdering, chunksMetadata -> chunksMetadata);
-
-        // Set the length of the first chunk to zero.
-        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
-        raf.seek(0);
-        raf.writeInt((int) mInputFile.length());
-
-        assertThrows(
-                MalformedEncryptedFileException.class,
-                () -> mTask.decryptFile(mInputFile, mKvOutput));
-    }
-
-    @Test
-    public void decryptFile_throwsForBadKey() throws Exception {
-        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
-
-        assertThrows(
-                AEADBadTagException.class,
-                () ->
-                        new BackupFileDecryptorTask(generateAesKey())
-                                .decryptFile(mInputFile, mFileOutput));
-    }
-
-    @Test
-    public void decryptFile_withExplicitStarts_throwsForMangledOrdering() throws Exception {
-        createEncryptedFileUsingExplicitStarts(
-                randomData(BACKUP_DATA_SIZE_BYTES),
-                chunkOrdering -> {
-                    ChunkOrdering ordering = CryptoTestUtils.clone(chunkOrdering);
-                    Arrays.sort(ordering.starts);
-                    return ordering;
-                });
-
-        assertThrows(
-                MessageDigestMismatchException.class,
-                () -> mTask.decryptFile(mInputFile, mFileOutput));
-    }
-
-    @Test
-    public void decryptFile_withExplicitStarts_noChunks_returnsNoData() throws Exception {
-        byte[] backupData = randomData(/*length=*/ 0);
-        createEncryptedFileUsingExplicitStarts(
-                backupData,
-                chunkOrdering -> {
-                    ChunkOrdering ordering = CryptoTestUtils.clone(chunkOrdering);
-                    ordering.starts = new int[0];
-                    return ordering;
-                });
-
-        mTask.decryptFile(mInputFile, mFileOutput);
-
-        assertThat(Files.readAllBytes(Paths.get(mOutputFile.toURI()))).isEqualTo(backupData);
-    }
-
-    @Test
-    public void decryptFile_throwsForMismatchedChecksum() throws Exception {
-        createEncryptedFileUsingExplicitStarts(
-                randomData(BACKUP_DATA_SIZE_BYTES),
-                chunkOrdering -> {
-                    ChunkOrdering ordering = CryptoTestUtils.clone(chunkOrdering);
-                    ordering.checksum =
-                            Arrays.copyOf(randomData(CHECKSUM_LENGTH_BYTES), CHECKSUM_LENGTH_BYTES);
-                    return ordering;
-                });
-
-        assertThrows(
-                MessageDigestMismatchException.class,
-                () -> mTask.decryptFile(mInputFile, mFileOutput));
-    }
-
-    @Test
-    public void decryptFile_throwsForBadChunksMetadataOffset() throws Exception {
-        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
-
-        // Replace the metadata with all 1s.
-        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
-        raf.seek(raf.length() - Long.BYTES);
-        int metadataOffset = (int) raf.readLong();
-        int metadataLength = (int) raf.length() - metadataOffset - Long.BYTES;
-
-        byte[] allOnes = new byte[metadataLength];
-        Arrays.fill(allOnes, (byte) 1);
-
-        raf.seek(metadataOffset);
-        raf.write(allOnes, /*off=*/ 0, metadataLength);
-
-        MalformedEncryptedFileException thrown =
-                expectThrows(
-                        MalformedEncryptedFileException.class,
-                        () -> mTask.decryptFile(mInputFile, mFileOutput));
-        assertThat(thrown)
-                .hasMessageThat()
-                .isEqualTo(
-                        "Could not read chunks metadata at position "
-                                + metadataOffset
-                                + " of file of "
-                                + raf.length()
-                                + " bytes");
-    }
-
-    @Test
-    public void decryptFile_throwsForChunksMetadataOffsetBeyondEndOfFile() throws Exception {
-        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
-
-        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
-        raf.seek(raf.length() - Long.BYTES);
-        raf.writeLong(raf.length());
-
-        MalformedEncryptedFileException thrown =
-                expectThrows(
-                        MalformedEncryptedFileException.class,
-                        () -> mTask.decryptFile(mInputFile, mFileOutput));
-        assertThat(thrown)
-                .hasMessageThat()
-                .isEqualTo(
-                        raf.length()
-                                + " is not valid position for chunks metadata in file of "
-                                + raf.length()
-                                + " bytes");
-    }
-
-    @Test
-    public void decryptFile_throwsForChunksMetadataOffsetBeforeBeginningOfFile() throws Exception {
-        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
-
-        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
-        raf.seek(raf.length() - Long.BYTES);
-        raf.writeLong(-1);
-
-        MalformedEncryptedFileException thrown =
-                expectThrows(
-                        MalformedEncryptedFileException.class,
-                        () -> mTask.decryptFile(mInputFile, mFileOutput));
-        assertThat(thrown)
-                .hasMessageThat()
-                .isEqualTo(
-                        "-1 is not valid position for chunks metadata in file of "
-                                + raf.length()
-                                + " bytes");
-    }
-
-    @Test
-    public void decryptFile_throwsForMangledChunks() throws Exception {
-        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
-
-        // Mess up some bits in a random byte
-        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
-        raf.seek(50);
-        byte fiftiethByte = raf.readByte();
-        raf.seek(50);
-        raf.write(~fiftiethByte);
-
-        assertThrows(AEADBadTagException.class, () -> mTask.decryptFile(mInputFile, mFileOutput));
-    }
-
-    @Test
-    public void decryptFile_throwsForBadChunkEncryptionKey() throws Exception {
-        mChunkEncryptionKey = generateAesKey();
-
-        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
-
-        assertThrows(AEADBadTagException.class, () -> mTask.decryptFile(mInputFile, mFileOutput));
-    }
-
-    @Test
-    public void decryptFile_throwsForUnsupportedCipherType() throws Exception {
-        createEncryptedFileUsingExplicitStarts(
-                randomData(BACKUP_DATA_SIZE_BYTES),
-                chunkOrdering -> chunkOrdering,
-                chunksMetadata -> {
-                    ChunksMetadata metadata = CryptoTestUtils.clone(chunksMetadata);
-                    metadata.cipherType = ChunksMetadataProto.UNKNOWN_CIPHER_TYPE;
-                    return metadata;
-                });
-
-        assertThrows(
-                UnsupportedEncryptedFileException.class,
-                () -> mTask.decryptFile(mInputFile, mFileOutput));
-    }
-
-    @Test
-    public void decryptFile_throwsForUnsupportedMessageDigestType() throws Exception {
-        createEncryptedFileUsingExplicitStarts(
-                randomData(BACKUP_DATA_SIZE_BYTES),
-                chunkOrdering -> chunkOrdering,
-                chunksMetadata -> {
-                    ChunksMetadata metadata = CryptoTestUtils.clone(chunksMetadata);
-                    metadata.checksumType = ChunksMetadataProto.UNKNOWN_CHECKSUM_TYPE;
-                    return metadata;
-                });
-
-        assertThrows(
-                UnsupportedEncryptedFileException.class,
-                () -> mTask.decryptFile(mInputFile, mFileOutput));
-    }
-
-    /**
-     * Creates an encrypted backup file from the given data.
-     *
-     * @param data The plaintext content.
-     */
-    private void createEncryptedFileUsingExplicitStarts(byte[] data) throws Exception {
-        createEncryptedFileUsingExplicitStarts(data, chunkOrdering -> chunkOrdering);
-    }
-
-    /**
-     * Creates an encrypted backup file from the given data.
-     *
-     * @param data The plaintext content.
-     * @param chunkOrderingTransformer Transforms the ordering before it's encrypted.
-     */
-    private void createEncryptedFileUsingExplicitStarts(
-            byte[] data, Transformer<ChunkOrdering> chunkOrderingTransformer) throws Exception {
-        createEncryptedFileUsingExplicitStarts(
-                data, chunkOrderingTransformer, chunksMetadata -> chunksMetadata);
-    }
-
-    /**
-     * Creates an encrypted backup file from the given data in mode {@link
-     * ChunksMetadataProto#EXPLICIT_STARTS}.
-     *
-     * @param data The plaintext content.
-     * @param chunkOrderingTransformer Transforms the ordering before it's encrypted.
-     * @param chunksMetadataTransformer Transforms the metadata before it's written.
-     */
-    private void createEncryptedFileUsingExplicitStarts(
-            byte[] data,
-            Transformer<ChunkOrdering> chunkOrderingTransformer,
-            Transformer<ChunksMetadata> chunksMetadataTransformer)
-            throws Exception {
-        Result result = backupFullData(data);
-
-        ArrayList<EncryptedChunk> chunks = new ArrayList<>(result.getNewChunks());
-        Collections.shuffle(chunks);
-        HashMap<ChunkHash, Integer> startPositions = new HashMap<>();
-
-        try (FileOutputStream fos = new FileOutputStream(mInputFile);
-                DataOutputStream dos = new DataOutputStream(fos)) {
-            int position = 0;
-
-            for (EncryptedChunk chunk : chunks) {
-                startPositions.put(chunk.key(), position);
-                dos.write(chunk.nonce());
-                dos.write(chunk.encryptedBytes());
-                position += chunk.nonce().length + chunk.encryptedBytes().length;
-            }
-
-            int[] starts = new int[chunks.size()];
-            List<ChunkHash> chunkListing = result.getAllChunks();
-
-            for (int i = 0; i < chunks.size(); i++) {
-                starts[i] = startPositions.get(chunkListing.get(i));
-            }
-
-            ChunkOrdering chunkOrdering = newChunkOrdering(starts, result.getDigest());
-            chunkOrdering = chunkOrderingTransformer.accept(chunkOrdering);
-
-            ChunksMetadata metadata =
-                    newChunksMetadata(
-                            ChunksMetadataProto.AES_256_GCM,
-                            ChunksMetadataProto.SHA_256,
-                            ChunksMetadataProto.EXPLICIT_STARTS,
-                            encrypt(chunkOrdering));
-            metadata = chunksMetadataTransformer.accept(metadata);
-
-            dos.write(MessageNano.toByteArray(metadata));
-            dos.writeLong(position);
-        }
-    }
-
-    /**
-     * Creates an encrypted backup file from the given data in mode {@link
-     * ChunksMetadataProto#INLINE_LENGTHS}.
-     *
-     * @param data The plaintext key value pairs to back up.
-     * @param chunkOrderingTransformer Transforms the ordering before it's encrypted.
-     * @param chunksMetadataTransformer Transforms the metadata before it's written.
-     */
-    private void createEncryptedFileUsingInlineLengths(
-            Set<KeyValuePair> data,
-            Transformer<ChunkOrdering> chunkOrderingTransformer,
-            Transformer<ChunksMetadata> chunksMetadataTransformer)
-            throws Exception {
-        Result result = backupKvData(data);
-
-        List<EncryptedChunk> chunks = new ArrayList<>(result.getNewChunks());
-        System.out.println("we have chunk count " + chunks.size());
-        Collections.shuffle(chunks);
-
-        try (FileOutputStream fos = new FileOutputStream(mInputFile);
-                DataOutputStream dos = new DataOutputStream(fos)) {
-            for (EncryptedChunk chunk : chunks) {
-                dos.writeInt(chunk.nonce().length + chunk.encryptedBytes().length);
-                dos.write(chunk.nonce());
-                dos.write(chunk.encryptedBytes());
-            }
-
-            ChunkOrdering chunkOrdering = newChunkOrdering(null, result.getDigest());
-            chunkOrdering = chunkOrderingTransformer.accept(chunkOrdering);
-
-            ChunksMetadata metadata =
-                    newChunksMetadata(
-                            ChunksMetadataProto.AES_256_GCM,
-                            ChunksMetadataProto.SHA_256,
-                            ChunksMetadataProto.INLINE_LENGTHS,
-                            encrypt(chunkOrdering));
-            metadata = chunksMetadataTransformer.accept(metadata);
-
-            int metadataStart = dos.size();
-            dos.write(MessageNano.toByteArray(metadata));
-            dos.writeLong(metadataStart);
-        }
-    }
-
-    /** Performs a full backup of the given data, and returns the chunks. */
-    private BackupEncrypter.Result backupFullData(byte[] data) throws Exception {
-        BackupStreamEncrypter encrypter =
-                new BackupStreamEncrypter(
-                        new ByteArrayInputStream(data),
-                        MIN_CHUNK_SIZE_BYTES,
-                        MAX_CHUNK_SIZE_BYTES,
-                        AVERAGE_CHUNK_SIZE_BYTES);
-        return encrypter.backup(
-                mChunkEncryptionKey,
-                randomData(FingerprintMixer.SALT_LENGTH_BYTES),
-                new HashSet<>());
-    }
-
-    private Result backupKvData(Set<KeyValuePair> data) throws Exception {
-        ShadowBackupDataInput.reset();
-        for (KeyValuePair pair : data) {
-            ShadowBackupDataInput.addEntity(pair.key, pair.value);
-        }
-        KvBackupEncrypter encrypter =
-                new KvBackupEncrypter(new BackupDataInput(NULL_FILE_DESCRIPTOR));
-        return encrypter.backup(
-                mChunkEncryptionKey,
-                randomData(FingerprintMixer.SALT_LENGTH_BYTES),
-                Collections.EMPTY_SET);
-    }
-
-    /** Encrypts {@code chunkOrdering} using {@link #mTertiaryKey}. */
-    private byte[] encrypt(ChunkOrdering chunkOrdering) throws Exception {
-        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
-        byte[] nonce = randomData(GCM_NONCE_LENGTH_BYTES);
-        cipher.init(
-                Cipher.ENCRYPT_MODE,
-                mTertiaryKey,
-                new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE, nonce));
-        byte[] nanoBytes = MessageNano.toByteArray(chunkOrdering);
-        byte[] encryptedBytes = cipher.doFinal(nanoBytes);
-
-        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-            out.write(nonce);
-            out.write(encryptedBytes);
-            return out.toByteArray();
-        }
-    }
-
-    /** Returns {@code length} random bytes. */
-    private byte[] randomData(int length) {
-        byte[] data = new byte[length];
-        mRandom.nextBytes(data);
-        return data;
-    }
-
-    private static ImmutableMap<String, String> asMap(Collection<KeyValuePair> pairs) {
-        ImmutableMap.Builder<String, String> map = ImmutableMap.builder();
-        for (KeyValuePair pair : pairs) {
-            map.put(pair.key, new String(pair.value, Charset.forName("UTF-8")));
-        }
-        return map.build();
-    }
-
-    private interface Transformer<T> {
-        T accept(T t);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypterTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypterTest.java
deleted file mode 100644
index 21c4e07..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypterTest.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.EncryptedChunk;
-import com.android.server.backup.testing.CryptoTestUtils;
-import com.android.server.backup.testing.RandomInputStream;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.ByteArrayInputStream;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-
-import javax.crypto.SecretKey;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class BackupStreamEncrypterTest {
-    private static final int SALT_LENGTH = 32;
-    private static final int BITS_PER_BYTE = 8;
-    private static final int BYTES_PER_KILOBYTE = 1024;
-    private static final int BYTES_PER_MEGABYTE = 1024 * 1024;
-    private static final int MIN_CHUNK_SIZE = 2 * BYTES_PER_KILOBYTE;
-    private static final int AVERAGE_CHUNK_SIZE = 4 * BYTES_PER_KILOBYTE;
-    private static final int MAX_CHUNK_SIZE = 64 * BYTES_PER_KILOBYTE;
-    private static final int BACKUP_SIZE = 2 * BYTES_PER_MEGABYTE;
-    private static final int SMALL_BACKUP_SIZE = BYTES_PER_KILOBYTE;
-    // 16 bytes for the mac. iv is encoded in a separate field.
-    private static final int BYTES_OVERHEAD_PER_CHUNK = 16;
-    private static final int MESSAGE_DIGEST_SIZE_IN_BYTES = 256 / BITS_PER_BYTE;
-    private static final int RANDOM_SEED = 42;
-    private static final double TOLERANCE = 0.1;
-
-    private Random mRandom;
-    private SecretKey mSecretKey;
-    private byte[] mSalt;
-
-    @Before
-    public void setUp() throws Exception {
-        mSecretKey = CryptoTestUtils.generateAesKey();
-
-        mSalt = new byte[SALT_LENGTH];
-        // Make these tests deterministic
-        mRandom = new Random(RANDOM_SEED);
-        mRandom.nextBytes(mSalt);
-    }
-
-    @Test
-    public void testBackup_producesChunksOfTheGivenAverageSize() throws Exception {
-        BackupEncrypter.Result result = runBackup(BACKUP_SIZE);
-
-        long totalSize = 0;
-        for (EncryptedChunk chunk : result.getNewChunks()) {
-            totalSize += chunk.encryptedBytes().length;
-        }
-
-        double meanSize = totalSize / result.getNewChunks().size();
-        double expectedChunkSize = AVERAGE_CHUNK_SIZE + BYTES_OVERHEAD_PER_CHUNK;
-        assertThat(Math.abs(meanSize - expectedChunkSize) / expectedChunkSize)
-                .isLessThan(TOLERANCE);
-    }
-
-    @Test
-    public void testBackup_producesNoChunksSmallerThanMinSize() throws Exception {
-        BackupEncrypter.Result result = runBackup(BACKUP_SIZE);
-        List<EncryptedChunk> chunks = result.getNewChunks();
-
-        // Last chunk could be smaller, depending on the file size and how it is chunked
-        for (EncryptedChunk chunk : chunks.subList(0, chunks.size() - 2)) {
-            assertThat(chunk.encryptedBytes().length)
-                    .isAtLeast(MIN_CHUNK_SIZE + BYTES_OVERHEAD_PER_CHUNK);
-        }
-    }
-
-    @Test
-    public void testBackup_producesNoChunksLargerThanMaxSize() throws Exception {
-        BackupEncrypter.Result result = runBackup(BACKUP_SIZE);
-        List<EncryptedChunk> chunks = result.getNewChunks();
-
-        for (EncryptedChunk chunk : chunks) {
-            assertThat(chunk.encryptedBytes().length)
-                    .isAtMost(MAX_CHUNK_SIZE + BYTES_OVERHEAD_PER_CHUNK);
-        }
-    }
-
-    @Test
-    public void testBackup_producesAFileOfTheExpectedSize() throws Exception {
-        BackupEncrypter.Result result = runBackup(BACKUP_SIZE);
-        HashMap<ChunkHash, EncryptedChunk> chunksBySha256 =
-                chunksIndexedByKey(result.getNewChunks());
-
-        int expectedSize = BACKUP_SIZE + result.getAllChunks().size() * BYTES_OVERHEAD_PER_CHUNK;
-        int size = 0;
-        for (ChunkHash byteString : result.getAllChunks()) {
-            size += chunksBySha256.get(byteString).encryptedBytes().length;
-        }
-        assertThat(size).isEqualTo(expectedSize);
-    }
-
-    @Test
-    public void testBackup_forSameFile_producesNoNewChunks() throws Exception {
-        byte[] backupData = getRandomData(BACKUP_SIZE);
-        BackupEncrypter.Result result = runBackup(backupData, ImmutableList.of());
-
-        BackupEncrypter.Result incrementalResult = runBackup(backupData, result.getAllChunks());
-
-        assertThat(incrementalResult.getNewChunks()).isEmpty();
-    }
-
-    @Test
-    public void testBackup_onlyUpdatesChangedChunks() throws Exception {
-        byte[] backupData = getRandomData(BACKUP_SIZE);
-        BackupEncrypter.Result result = runBackup(backupData, ImmutableList.of());
-
-        // Let's update the 2nd and 5th chunk
-        backupData[positionOfChunk(result, 1)]++;
-        backupData[positionOfChunk(result, 4)]++;
-        BackupEncrypter.Result incrementalResult = runBackup(backupData, result.getAllChunks());
-
-        assertThat(incrementalResult.getNewChunks()).hasSize(2);
-    }
-
-    @Test
-    public void testBackup_doesNotIncludeUpdatedChunksInNewListing() throws Exception {
-        byte[] backupData = getRandomData(BACKUP_SIZE);
-        BackupEncrypter.Result result = runBackup(backupData, ImmutableList.of());
-
-        // Let's update the 2nd and 5th chunk
-        backupData[positionOfChunk(result, 1)]++;
-        backupData[positionOfChunk(result, 4)]++;
-        BackupEncrypter.Result incrementalResult = runBackup(backupData, result.getAllChunks());
-
-        List<EncryptedChunk> newChunks = incrementalResult.getNewChunks();
-        List<ChunkHash> chunkListing = result.getAllChunks();
-        assertThat(newChunks).doesNotContain(chunkListing.get(1));
-        assertThat(newChunks).doesNotContain(chunkListing.get(4));
-    }
-
-    @Test
-    public void testBackup_includesUnchangedChunksInNewListing() throws Exception {
-        byte[] backupData = getRandomData(BACKUP_SIZE);
-        BackupEncrypter.Result result = runBackup(backupData, ImmutableList.of());
-
-        // Let's update the 2nd and 5th chunk
-        backupData[positionOfChunk(result, 1)]++;
-        backupData[positionOfChunk(result, 4)]++;
-        BackupEncrypter.Result incrementalResult = runBackup(backupData, result.getAllChunks());
-
-        HashSet<ChunkHash> chunksPresentInIncremental =
-                new HashSet<>(incrementalResult.getAllChunks());
-        chunksPresentInIncremental.removeAll(result.getAllChunks());
-
-        assertThat(chunksPresentInIncremental).hasSize(2);
-    }
-
-    @Test
-    public void testBackup_forSameData_createsSameDigest() throws Exception {
-        byte[] backupData = getRandomData(SMALL_BACKUP_SIZE);
-
-        BackupEncrypter.Result result = runBackup(backupData, ImmutableList.of());
-        BackupEncrypter.Result result2 = runBackup(backupData, ImmutableList.of());
-        assertThat(result.getDigest()).isEqualTo(result2.getDigest());
-    }
-
-    @Test
-    public void testBackup_forDifferentData_createsDifferentDigest() throws Exception {
-        byte[] backup1Data = getRandomData(SMALL_BACKUP_SIZE);
-        byte[] backup2Data = getRandomData(SMALL_BACKUP_SIZE);
-
-        BackupEncrypter.Result result = runBackup(backup1Data, ImmutableList.of());
-        BackupEncrypter.Result result2 = runBackup(backup2Data, ImmutableList.of());
-        assertThat(result.getDigest()).isNotEqualTo(result2.getDigest());
-    }
-
-    @Test
-    public void testBackup_createsDigestOf32Bytes() throws Exception {
-        assertThat(runBackup(getRandomData(SMALL_BACKUP_SIZE), ImmutableList.of()).getDigest())
-                .hasLength(MESSAGE_DIGEST_SIZE_IN_BYTES);
-    }
-
-    private byte[] getRandomData(int size) throws Exception {
-        RandomInputStream randomInputStream = new RandomInputStream(mRandom, size);
-        byte[] backupData = new byte[size];
-        randomInputStream.read(backupData);
-        return backupData;
-    }
-
-    private BackupEncrypter.Result runBackup(int backupSize) throws Exception {
-        RandomInputStream dataStream = new RandomInputStream(mRandom, backupSize);
-        BackupStreamEncrypter task =
-                new BackupStreamEncrypter(
-                        dataStream, MIN_CHUNK_SIZE, MAX_CHUNK_SIZE, AVERAGE_CHUNK_SIZE);
-        return task.backup(mSecretKey, mSalt, ImmutableSet.of());
-    }
-
-    private BackupEncrypter.Result runBackup(byte[] data, List<ChunkHash> existingChunks)
-            throws Exception {
-        ByteArrayInputStream dataStream = new ByteArrayInputStream(data);
-        BackupStreamEncrypter task =
-                new BackupStreamEncrypter(
-                        dataStream, MIN_CHUNK_SIZE, MAX_CHUNK_SIZE, AVERAGE_CHUNK_SIZE);
-        return task.backup(mSecretKey, mSalt, ImmutableSet.copyOf(existingChunks));
-    }
-
-    /** Returns a {@link HashMap} of the chunks, indexed by the SHA-256 Mac key. */
-    private static HashMap<ChunkHash, EncryptedChunk> chunksIndexedByKey(
-            List<EncryptedChunk> chunks) {
-        HashMap<ChunkHash, EncryptedChunk> chunksByKey = new HashMap<>();
-        for (EncryptedChunk chunk : chunks) {
-            chunksByKey.put(chunk.key(), chunk);
-        }
-        return chunksByKey;
-    }
-
-    /**
-     * Returns the start position of the chunk in the plaintext backup data.
-     *
-     * @param result The result from a backup.
-     * @param index The index of the chunk in question.
-     * @return the start position.
-     */
-    private static int positionOfChunk(BackupEncrypter.Result result, int index) {
-        HashMap<ChunkHash, EncryptedChunk> byKey = chunksIndexedByKey(result.getNewChunks());
-        List<ChunkHash> listing = result.getAllChunks();
-
-        int position = 0;
-        for (int i = 0; i < index - 1; i++) {
-            EncryptedChunk chunk = byKey.get(listing.get(i));
-            position += chunk.encryptedBytes().length - BYTES_OVERHEAD_PER_CHUNK;
-        }
-
-        return position;
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTaskTest.java
deleted file mode 100644
index 81bfce1..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTaskTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.spy;
-
-import android.content.Context;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.chunking.ProtoStore;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
-import com.android.server.backup.encryption.protos.nano.KeyValueListingProto.KeyValueListing;
-import com.android.server.backup.encryption.storage.BackupEncryptionDb;
-import com.android.server.backup.encryption.storage.TertiaryKey;
-import com.android.server.backup.encryption.storage.TertiaryKeysTable;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class ClearCryptoStateTaskTest {
-    private static final String TEST_PACKAGE_NAME = "com.android.example";
-
-    private ClearCryptoStateTask mClearCryptoStateTask;
-    private CryptoSettings mCryptoSettings;
-    private Context mApplication;
-
-    @Before
-    public void setUp() {
-        mApplication = ApplicationProvider.getApplicationContext();
-        mCryptoSettings = spy(CryptoSettings.getInstanceForTesting(mApplication));
-        mClearCryptoStateTask = new ClearCryptoStateTask(mApplication, mCryptoSettings);
-    }
-
-    @Test
-    public void run_clearsChunkListingProtoState() throws Exception {
-        String packageName = TEST_PACKAGE_NAME;
-        ChunkListing chunkListing = new ChunkListing();
-        ProtoStore.createChunkListingStore(mApplication).saveProto(packageName, chunkListing);
-
-        mClearCryptoStateTask.run();
-
-        assertThat(
-                        ProtoStore.createChunkListingStore(mApplication)
-                                .loadProto(packageName)
-                                .isPresent())
-                .isFalse();
-    }
-
-    @Test
-    public void run_clearsKeyValueProtoState() throws Exception {
-        String packageName = TEST_PACKAGE_NAME;
-        KeyValueListing keyValueListing = new KeyValueListing();
-        ProtoStore.createKeyValueListingStore(mApplication).saveProto(packageName, keyValueListing);
-
-        mClearCryptoStateTask.run();
-
-        assertThat(
-                        ProtoStore.createKeyValueListingStore(mApplication)
-                                .loadProto(packageName)
-                                .isPresent())
-                .isFalse();
-    }
-
-    @Test
-    public void run_clearsTertiaryKeysTable() throws Exception {
-        String secondaryKeyAlias = "bob";
-        TertiaryKeysTable tertiaryKeysTable =
-                BackupEncryptionDb.newInstance(mApplication).getTertiaryKeysTable();
-        tertiaryKeysTable.addKey(
-                new TertiaryKey(
-                        secondaryKeyAlias, "packageName", /*wrappedKeyBytes=*/ new byte[0]));
-
-        mClearCryptoStateTask.run();
-
-        assertThat(tertiaryKeysTable.getAllKeys(secondaryKeyAlias)).isEmpty();
-    }
-
-    @Test
-    public void run_clearsSettings() {
-        mCryptoSettings.setSecondaryLastRotated(100001);
-
-        mClearCryptoStateTask.run();
-
-        assertThat(mCryptoSettings.getSecondaryLastRotated().isPresent()).isFalse();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java
deleted file mode 100644
index 23d6e34..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.AES_256_GCM;
-import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
-import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.SHA_256;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.BackupFileBuilder;
-import com.android.server.backup.encryption.chunking.EncryptedChunk;
-import com.android.server.backup.encryption.chunking.EncryptedChunkEncoder;
-import com.android.server.backup.encryption.chunking.LengthlessEncryptedChunkEncoder;
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.keys.TertiaryKeyGenerator;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkOrdering;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunksMetadata;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto.WrappedKey;
-import com.android.server.backup.encryption.tasks.BackupEncrypter.Result;
-import com.android.server.backup.testing.CryptoTestUtils;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.protobuf.nano.MessageNano;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-import java.io.OutputStream;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.concurrent.CancellationException;
-
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
-
-@Config(shadows = {EncryptedBackupTaskTest.ShadowBackupFileBuilder.class})
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class EncryptedBackupTaskTest {
-
-    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
-    private static final int GCM_NONCE_LENGTH_BYTES = 12;
-    private static final int GCM_TAG_LENGTH_BYTES = 16;
-    private static final int BITS_PER_BYTE = 8;
-
-    private static final byte[] TEST_FINGERPRINT_MIXER_SALT =
-            Arrays.copyOf(new byte[] {22}, ChunkHash.HASH_LENGTH_BYTES);
-
-    private static final byte[] TEST_NONCE =
-            Arrays.copyOf(new byte[] {55}, EncryptedChunk.NONCE_LENGTH_BYTES);
-
-    private static final ChunkHash TEST_HASH_1 =
-            new ChunkHash(Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES));
-    private static final ChunkHash TEST_HASH_2 =
-            new ChunkHash(Arrays.copyOf(new byte[] {2}, ChunkHash.HASH_LENGTH_BYTES));
-    private static final ChunkHash TEST_HASH_3 =
-            new ChunkHash(Arrays.copyOf(new byte[] {3}, ChunkHash.HASH_LENGTH_BYTES));
-
-    private static final EncryptedChunk TEST_CHUNK_1 =
-            EncryptedChunk.create(TEST_HASH_1, TEST_NONCE, new byte[] {1, 2, 3, 4, 5});
-    private static final EncryptedChunk TEST_CHUNK_2 =
-            EncryptedChunk.create(TEST_HASH_2, TEST_NONCE, new byte[] {6, 7, 8, 9, 10});
-    private static final EncryptedChunk TEST_CHUNK_3 =
-            EncryptedChunk.create(TEST_HASH_3, TEST_NONCE, new byte[] {11, 12, 13, 14, 15});
-
-    private static final byte[] TEST_CHECKSUM = Arrays.copyOf(new byte[] {10}, 258 / 8);
-    private static final String TEST_PACKAGE_NAME = "com.example.package";
-    private static final String TEST_OLD_DOCUMENT_ID = "old_doc_1";
-    private static final String TEST_NEW_DOCUMENT_ID = "new_doc_1";
-
-    @Captor private ArgumentCaptor<ChunksMetadata> mMetadataCaptor;
-
-    @Mock private CryptoBackupServer mCryptoBackupServer;
-    @Mock private BackupEncrypter mBackupEncrypter;
-    @Mock private BackupFileBuilder mBackupFileBuilder;
-
-    private ChunkListing mOldChunkListing;
-    private SecretKey mTertiaryKey;
-    private WrappedKey mWrappedTertiaryKey;
-    private EncryptedChunkEncoder mEncryptedChunkEncoder;
-    private EncryptedBackupTask mTask;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        SecureRandom secureRandom = new SecureRandom();
-        mTertiaryKey = new TertiaryKeyGenerator(secureRandom).generate();
-        mWrappedTertiaryKey = new WrappedKey();
-
-        mEncryptedChunkEncoder = new LengthlessEncryptedChunkEncoder();
-
-        ShadowBackupFileBuilder.sInstance = mBackupFileBuilder;
-
-        mTask =
-                new EncryptedBackupTask(
-                        mCryptoBackupServer, secureRandom, TEST_PACKAGE_NAME, mBackupEncrypter);
-    }
-
-    @Test
-    public void performNonIncrementalBackup_performsBackup() throws Exception {
-        setUpWithoutExistingBackup();
-
-        // Chunk listing and ordering don't matter for this test.
-        when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(new ChunkListing());
-        when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
-
-        when(mCryptoBackupServer.uploadNonIncrementalBackup(eq(TEST_PACKAGE_NAME), any(), any()))
-                .thenReturn(TEST_NEW_DOCUMENT_ID);
-
-        mTask.performNonIncrementalBackup(
-                mTertiaryKey, mWrappedTertiaryKey, TEST_FINGERPRINT_MIXER_SALT);
-
-        verify(mBackupFileBuilder)
-                .writeChunks(
-                        ImmutableList.of(TEST_HASH_1, TEST_HASH_2),
-                        ImmutableMap.of(TEST_HASH_1, TEST_CHUNK_1, TEST_HASH_2, TEST_CHUNK_2));
-        verify(mBackupFileBuilder).finish(any());
-        verify(mCryptoBackupServer)
-                .uploadNonIncrementalBackup(eq(TEST_PACKAGE_NAME), any(), eq(mWrappedTertiaryKey));
-    }
-
-    @Test
-    public void performIncrementalBackup_performsBackup() throws Exception {
-        setUpWithExistingBackup();
-
-        // Chunk listing and ordering don't matter for this test.
-        when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(new ChunkListing());
-        when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
-
-        when(mCryptoBackupServer.uploadIncrementalBackup(
-                        eq(TEST_PACKAGE_NAME), eq(TEST_OLD_DOCUMENT_ID), any(), any()))
-                .thenReturn(TEST_NEW_DOCUMENT_ID);
-
-        mTask.performIncrementalBackup(mTertiaryKey, mWrappedTertiaryKey, mOldChunkListing);
-
-        verify(mBackupFileBuilder)
-                .writeChunks(
-                        ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_3),
-                        ImmutableMap.of(TEST_HASH_2, TEST_CHUNK_2));
-        verify(mBackupFileBuilder).finish(any());
-        verify(mCryptoBackupServer)
-                .uploadIncrementalBackup(
-                        eq(TEST_PACKAGE_NAME),
-                        eq(TEST_OLD_DOCUMENT_ID),
-                        any(),
-                        eq(mWrappedTertiaryKey));
-    }
-
-    @Test
-    public void performIncrementalBackup_returnsNewChunkListingWithDocId() throws Exception {
-        setUpWithExistingBackup();
-
-        ChunkListing chunkListingWithoutDocId =
-                CryptoTestUtils.newChunkListingWithoutDocId(
-                        TEST_FINGERPRINT_MIXER_SALT,
-                        AES_256_GCM,
-                        CHUNK_ORDERING_TYPE_UNSPECIFIED,
-                        createChunkProtoFor(TEST_HASH_1, TEST_CHUNK_1),
-                        createChunkProtoFor(TEST_HASH_2, TEST_CHUNK_2));
-        when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(chunkListingWithoutDocId);
-
-        // Chunk ordering doesn't matter for this test.
-        when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
-
-        when(mCryptoBackupServer.uploadIncrementalBackup(
-                        eq(TEST_PACKAGE_NAME), eq(TEST_OLD_DOCUMENT_ID), any(), any()))
-                .thenReturn(TEST_NEW_DOCUMENT_ID);
-
-        ChunkListing actualChunkListing =
-                mTask.performIncrementalBackup(mTertiaryKey, mWrappedTertiaryKey, mOldChunkListing);
-
-        ChunkListing expectedChunkListing = CryptoTestUtils.clone(chunkListingWithoutDocId);
-        expectedChunkListing.documentId = TEST_NEW_DOCUMENT_ID;
-        assertChunkListingsAreEqual(actualChunkListing, expectedChunkListing);
-    }
-
-    @Test
-    public void performNonIncrementalBackup_returnsNewChunkListingWithDocId() throws Exception {
-        setUpWithoutExistingBackup();
-
-        ChunkListing chunkListingWithoutDocId =
-                CryptoTestUtils.newChunkListingWithoutDocId(
-                        TEST_FINGERPRINT_MIXER_SALT,
-                        AES_256_GCM,
-                        CHUNK_ORDERING_TYPE_UNSPECIFIED,
-                        createChunkProtoFor(TEST_HASH_1, TEST_CHUNK_1),
-                        createChunkProtoFor(TEST_HASH_2, TEST_CHUNK_2));
-        when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(chunkListingWithoutDocId);
-
-        // Chunk ordering doesn't matter for this test.
-        when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
-
-        when(mCryptoBackupServer.uploadNonIncrementalBackup(eq(TEST_PACKAGE_NAME), any(), any()))
-                .thenReturn(TEST_NEW_DOCUMENT_ID);
-
-        ChunkListing actualChunkListing =
-                mTask.performNonIncrementalBackup(
-                        mTertiaryKey, mWrappedTertiaryKey, TEST_FINGERPRINT_MIXER_SALT);
-
-        ChunkListing expectedChunkListing = CryptoTestUtils.clone(chunkListingWithoutDocId);
-        expectedChunkListing.documentId = TEST_NEW_DOCUMENT_ID;
-        assertChunkListingsAreEqual(actualChunkListing, expectedChunkListing);
-    }
-
-    @Test
-    public void performNonIncrementalBackup_buildsCorrectChunkMetadata() throws Exception {
-        setUpWithoutExistingBackup();
-
-        // Chunk listing doesn't matter for this test.
-        when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(new ChunkListing());
-
-        ChunkOrdering expectedOrdering =
-                CryptoTestUtils.newChunkOrdering(new int[10], TEST_CHECKSUM);
-        when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(expectedOrdering);
-
-        when(mCryptoBackupServer.uploadNonIncrementalBackup(eq(TEST_PACKAGE_NAME), any(), any()))
-                .thenReturn(TEST_NEW_DOCUMENT_ID);
-
-        mTask.performNonIncrementalBackup(
-                mTertiaryKey, mWrappedTertiaryKey, TEST_FINGERPRINT_MIXER_SALT);
-
-        verify(mBackupFileBuilder).finish(mMetadataCaptor.capture());
-
-        ChunksMetadata actualMetadata = mMetadataCaptor.getValue();
-        assertThat(actualMetadata.checksumType).isEqualTo(SHA_256);
-        assertThat(actualMetadata.cipherType).isEqualTo(AES_256_GCM);
-
-        ChunkOrdering actualOrdering = decryptChunkOrdering(actualMetadata.chunkOrdering);
-        assertThat(actualOrdering.checksum).isEqualTo(TEST_CHECKSUM);
-        assertThat(actualOrdering.starts).isEqualTo(expectedOrdering.starts);
-    }
-
-    @Test
-    public void cancel_incrementalBackup_doesNotUploadOrSaveChunkListing() throws Exception {
-        setUpWithExistingBackup();
-
-        // Chunk listing and ordering don't matter for this test.
-        when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(new ChunkListing());
-        when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
-
-        mTask.cancel();
-        assertThrows(
-                CancellationException.class,
-                () ->
-                        mTask.performIncrementalBackup(
-                                mTertiaryKey, mWrappedTertiaryKey, mOldChunkListing));
-
-        verify(mCryptoBackupServer, never()).uploadIncrementalBackup(any(), any(), any(), any());
-        verify(mCryptoBackupServer, never()).uploadNonIncrementalBackup(any(), any(), any());
-    }
-
-    @Test
-    public void cancel_nonIncrementalBackup_doesNotUploadOrSaveChunkListing() throws Exception {
-        setUpWithoutExistingBackup();
-
-        // Chunk listing and ordering don't matter for this test.
-        when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(new ChunkListing());
-        when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
-
-        mTask.cancel();
-        assertThrows(
-                CancellationException.class,
-                () ->
-                        mTask.performNonIncrementalBackup(
-                                mTertiaryKey, mWrappedTertiaryKey, TEST_FINGERPRINT_MIXER_SALT));
-
-        verify(mCryptoBackupServer, never()).uploadIncrementalBackup(any(), any(), any(), any());
-        verify(mCryptoBackupServer, never()).uploadNonIncrementalBackup(any(), any(), any());
-    }
-
-    /** Sets up a backup of [CHUNK 1][CHUNK 2] with no existing data. */
-    private void setUpWithoutExistingBackup() throws Exception {
-        Result result =
-                new Result(
-                        ImmutableList.of(TEST_HASH_1, TEST_HASH_2),
-                        ImmutableList.of(TEST_CHUNK_1, TEST_CHUNK_2),
-                        TEST_CHECKSUM);
-        when(mBackupEncrypter.backup(any(), eq(TEST_FINGERPRINT_MIXER_SALT), eq(ImmutableSet.of())))
-                .thenReturn(result);
-    }
-
-    /**
-     * Sets up a backup of [CHUNK 1][CHUNK 2][CHUNK 3] where the previous backup contained [CHUNK
-     * 1][CHUNK 3].
-     */
-    private void setUpWithExistingBackup() throws Exception {
-        mOldChunkListing =
-                CryptoTestUtils.newChunkListing(
-                        TEST_OLD_DOCUMENT_ID,
-                        TEST_FINGERPRINT_MIXER_SALT,
-                        AES_256_GCM,
-                        CHUNK_ORDERING_TYPE_UNSPECIFIED,
-                        createChunkProtoFor(TEST_HASH_1, TEST_CHUNK_1),
-                        createChunkProtoFor(TEST_HASH_3, TEST_CHUNK_3));
-
-        Result result =
-                new Result(
-                        ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_3),
-                        ImmutableList.of(TEST_CHUNK_2),
-                        TEST_CHECKSUM);
-        when(mBackupEncrypter.backup(
-                        any(),
-                        eq(TEST_FINGERPRINT_MIXER_SALT),
-                        eq(ImmutableSet.of(TEST_HASH_1, TEST_HASH_3))))
-                .thenReturn(result);
-    }
-
-    private ChunksMetadataProto.Chunk createChunkProtoFor(
-            ChunkHash chunkHash, EncryptedChunk encryptedChunk) {
-        return CryptoTestUtils.newChunk(
-                chunkHash, mEncryptedChunkEncoder.getEncodedLengthOfChunk(encryptedChunk));
-    }
-
-    private ChunkOrdering decryptChunkOrdering(byte[] encryptedOrdering) throws Exception {
-        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
-        cipher.init(
-                Cipher.DECRYPT_MODE,
-                mTertiaryKey,
-                new GCMParameterSpec(
-                        GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE,
-                        encryptedOrdering,
-                        /*offset=*/ 0,
-                        GCM_NONCE_LENGTH_BYTES));
-        byte[] decrypted =
-                cipher.doFinal(
-                        encryptedOrdering,
-                        GCM_NONCE_LENGTH_BYTES,
-                        encryptedOrdering.length - GCM_NONCE_LENGTH_BYTES);
-        return ChunkOrdering.parseFrom(decrypted);
-    }
-
-    // This method is needed because nano protobuf generated classes dont implmenent
-    // .equals
-    private void assertChunkListingsAreEqual(ChunkListing a, ChunkListing b) {
-        byte[] aBytes = MessageNano.toByteArray(a);
-        byte[] bBytes = MessageNano.toByteArray(b);
-
-        assertThat(aBytes).isEqualTo(bBytes);
-    }
-
-    @Implements(BackupFileBuilder.class)
-    public static class ShadowBackupFileBuilder {
-
-        private static BackupFileBuilder sInstance;
-
-        @Implementation
-        public static BackupFileBuilder createForNonIncremental(OutputStream outputStream) {
-            return sInstance;
-        }
-
-        @Implementation
-        public static BackupFileBuilder createForIncremental(
-                OutputStream outputStream, ChunkListing oldChunkListing) {
-            return sInstance;
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java
deleted file mode 100644
index 675d03f..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java
+++ /dev/null
@@ -1,387 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.testng.Assert.assertThrows;
-
-import android.annotation.Nullable;
-import android.app.backup.BackupTransport;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.backup.encryption.FullBackupDataProcessor;
-import com.android.server.backup.encryption.chunking.ProtoStore;
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.TertiaryKeyManager;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.testing.QueuingNonAutomaticExecutorService;
-
-import com.google.common.io.ByteStreams;
-import com.google.common.primitives.Bytes;
-
-import org.junit.After;
-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.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.SecureRandom;
-
-import javax.crypto.spec.SecretKeySpec;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-@Config(
-        shadows = {
-            EncryptedFullBackupDataProcessorTest.ShadowEncryptedFullBackupTask.class,
-        })
-public class EncryptedFullBackupDataProcessorTest {
-
-    private static final String KEY_GENERATOR_ALGORITHM = "AES";
-
-    private static final String TEST_PACKAGE = "com.example.app1";
-    private static final byte[] TEST_DATA_1 = {1, 2, 3, 4};
-    private static final byte[] TEST_DATA_2 = {5, 6, 7, 8};
-
-    private final RecoverableKeyStoreSecondaryKey mTestSecondaryKey =
-            new RecoverableKeyStoreSecondaryKey(
-                    /*alias=*/ "test_key",
-                    new SecretKeySpec(
-                            new byte[] {
-                                1, 2, 3,
-                            },
-                            KEY_GENERATOR_ALGORITHM));
-
-    private QueuingNonAutomaticExecutorService mExecutorService;
-    private FullBackupDataProcessor mFullBackupDataProcessor;
-    @Mock private FullBackupDataProcessor.FullBackupCallbacks mFullBackupCallbacks;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mExecutorService = new QueuingNonAutomaticExecutorService();
-        mFullBackupDataProcessor =
-                new EncryptedFullBackupDataProcessor(
-                        ApplicationProvider.getApplicationContext(),
-                        mExecutorService,
-                        mock(CryptoBackupServer.class),
-                        new SecureRandom(),
-                        mTestSecondaryKey,
-                        TEST_PACKAGE);
-    }
-
-    @After
-    public void tearDown() {
-        ShadowEncryptedFullBackupTask.reset();
-    }
-
-    @Test
-    public void initiate_callTwice_throws() throws Exception {
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10]));
-
-        assertThrows(
-                IllegalStateException.class,
-                () -> mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10])));
-    }
-
-    @Test
-    public void pushData_writesDataToTask() throws Exception {
-        byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
-
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
-        finishBackupTask();
-        mFullBackupDataProcessor.finish();
-
-        byte[] result = ByteStreams.toByteArray(ShadowEncryptedFullBackupTask.sInputStream);
-        assertThat(result).isEqualTo(Bytes.concat(TEST_DATA_1, TEST_DATA_2));
-    }
-
-    @Test
-    public void pushData_noError_returnsOk() throws Exception {
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
-        mFullBackupDataProcessor.start();
-        int result = mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        finishBackupTask();
-        mFullBackupDataProcessor.finish();
-
-        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_OK);
-    }
-
-    @Test
-    public void pushData_ioExceptionOnCopy_returnsError() throws Exception {
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
-        mFullBackupDataProcessor.start();
-
-        // Close the stream so there's an IO error when the processor tries to write to it.
-        ShadowEncryptedFullBackupTask.sInputStream.close();
-        int result = mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-
-        finishBackupTask();
-        mFullBackupDataProcessor.finish();
-
-        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
-    }
-
-    @Test
-    public void pushData_exceptionDuringUpload_returnsError() throws Exception {
-        byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
-
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        finishBackupTaskWithException(new IOException("Test exception"));
-        int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
-
-        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
-    }
-
-    @Test
-    public void pushData_quotaExceptionDuringUpload_doesNotLogAndReturnsQuotaExceeded()
-            throws Exception {
-        mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
-        byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
-
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        finishBackupTaskWithException(new SizeQuotaExceededException());
-        int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
-
-        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
-
-        verify(mFullBackupCallbacks, never()).onSuccess();
-        verify(mFullBackupCallbacks, never())
-                .onTransferFailed(); // FullBackupSession will handle this.
-    }
-
-    @Test
-    public void pushData_unexpectedEncryptedBackup_logs() throws Exception {
-        byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
-
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        finishBackupTaskWithException(new GeneralSecurityException());
-        int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
-
-        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
-    }
-
-    @Test
-    public void pushData_permanentExceptionDuringUpload_callsErrorCallback() throws Exception {
-        mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
-        byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
-
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        finishBackupTaskWithException(new IOException());
-        mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
-
-        verify(mFullBackupCallbacks, never()).onSuccess();
-        verify(mFullBackupCallbacks).onTransferFailed();
-    }
-
-    @Test
-    public void pushData_beforeInitiate_throws() {
-        assertThrows(
-                IllegalStateException.class,
-                () -> mFullBackupDataProcessor.pushData(/*numBytes=*/ 10));
-    }
-
-    @Test
-    public void cancel_cancelsTask() throws Exception {
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        mFullBackupDataProcessor.cancel();
-
-        assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
-    }
-
-    @Test
-    public void cancel_beforeInitiate_throws() {
-        assertThrows(IllegalStateException.class, () -> mFullBackupDataProcessor.cancel());
-    }
-
-    @Test
-    public void finish_noException_returnsTransportOk() throws Exception {
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        finishBackupTask();
-        int result = mFullBackupDataProcessor.finish();
-
-        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_OK);
-    }
-
-    @Test
-    public void finish_exceptionDuringUpload_returnsTransportError() throws Exception {
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        finishBackupTaskWithException(new IOException("Test exception"));
-        int result = mFullBackupDataProcessor.finish();
-
-        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
-    }
-
-    @Test
-    public void finish_successfulBackup_callsSuccessCallback() throws Exception {
-        mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
-
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        finishBackupTask();
-        mFullBackupDataProcessor.finish();
-
-        verify(mFullBackupCallbacks).onSuccess();
-        verify(mFullBackupCallbacks, never()).onTransferFailed();
-    }
-
-    @Test
-    public void finish_backupFailedWithPermanentError_callsErrorCallback() throws Exception {
-        mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
-
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        finishBackupTaskWithException(new IOException());
-        mFullBackupDataProcessor.finish();
-
-        verify(mFullBackupCallbacks, never()).onSuccess();
-        verify(mFullBackupCallbacks).onTransferFailed();
-    }
-
-    @Test
-    public void finish_backupFailedWithQuotaException_doesNotCallbackAndReturnsQuotaExceeded()
-            throws Exception {
-        mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
-
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        finishBackupTaskWithException(new SizeQuotaExceededException());
-        int result = mFullBackupDataProcessor.finish();
-
-        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
-        verify(mFullBackupCallbacks, never()).onSuccess();
-        verify(mFullBackupCallbacks, never())
-                .onTransferFailed(); // FullBackupSession will handle this.
-    }
-
-    @Test
-    public void finish_beforeInitiate_throws() {
-        assertThrows(IllegalStateException.class, () -> mFullBackupDataProcessor.finish());
-    }
-
-    @Test
-    public void handleCheckSizeRejectionZeroBytes_cancelsTask() throws Exception {
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10]));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.handleCheckSizeRejectionZeroBytes();
-
-        assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
-    }
-
-    @Test
-    public void handleCheckSizeRejectionQuotaExceeded_cancelsTask() throws Exception {
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        mFullBackupDataProcessor.handleCheckSizeRejectionQuotaExceeded();
-
-        assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
-    }
-
-    @Test
-    public void handleSendBytesQuotaExceeded_cancelsTask() throws Exception {
-        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
-        mFullBackupDataProcessor.start();
-        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
-        mFullBackupDataProcessor.handleSendBytesQuotaExceeded();
-
-        assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
-    }
-
-    private void finishBackupTask() {
-        mExecutorService.runNext();
-    }
-
-    private void finishBackupTaskWithException(Exception exception) {
-        ShadowEncryptedFullBackupTask.sOnCallException = exception;
-        finishBackupTask();
-    }
-
-    @Implements(EncryptedFullBackupTask.class)
-    public static class ShadowEncryptedFullBackupTask {
-
-        private static InputStream sInputStream;
-        @Nullable private static Exception sOnCallException;
-        private static boolean sCancelled;
-
-        public void __constructor__(
-                ProtoStore<ChunksMetadataProto.ChunkListing> chunkListingStore,
-                TertiaryKeyManager tertiaryKeyManager,
-                EncryptedBackupTask task,
-                InputStream inputStream,
-                String packageName,
-                SecureRandom secureRandom) {
-            sInputStream = inputStream;
-        }
-
-        @Implementation
-        public Void call() throws Exception {
-            if (sOnCallException != null) {
-                throw sOnCallException;
-            }
-
-            return null;
-        }
-
-        @Implementation
-        public void cancel() {
-            sCancelled = true;
-        }
-
-        public static void reset() {
-            sOnCallException = null;
-            sCancelled = false;
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java
deleted file mode 100644
index bfc5d0d..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.ProtoStore;
-import com.android.server.backup.encryption.chunking.cdc.FingerprintMixer;
-import com.android.server.backup.encryption.keys.TertiaryKeyManager;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto.WrappedKey;
-import com.android.server.backup.testing.CryptoTestUtils;
-
-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.annotation.Config;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.Optional;
-
-import javax.crypto.SecretKey;
-
-@Config(shadows = {EncryptedBackupTaskTest.ShadowBackupFileBuilder.class})
-@RunWith(RobolectricTestRunner.class)
-public class EncryptedFullBackupTaskTest {
-    private static final String TEST_PACKAGE_NAME = "com.example.package";
-    private static final byte[] TEST_EXISTING_FINGERPRINT_MIXER_SALT =
-            Arrays.copyOf(new byte[] {11}, ChunkHash.HASH_LENGTH_BYTES);
-    private static final byte[] TEST_GENERATED_FINGERPRINT_MIXER_SALT =
-            Arrays.copyOf(new byte[] {22}, ChunkHash.HASH_LENGTH_BYTES);
-    private static final ChunkHash TEST_CHUNK_HASH_1 =
-            new ChunkHash(Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES));
-    private static final ChunkHash TEST_CHUNK_HASH_2 =
-            new ChunkHash(Arrays.copyOf(new byte[] {2}, ChunkHash.HASH_LENGTH_BYTES));
-    private static final int TEST_CHUNK_LENGTH_1 = 20;
-    private static final int TEST_CHUNK_LENGTH_2 = 40;
-
-    @Mock private ProtoStore<ChunkListing> mChunkListingStore;
-    @Mock private TertiaryKeyManager mTertiaryKeyManager;
-    @Mock private InputStream mInputStream;
-    @Mock private EncryptedBackupTask mEncryptedBackupTask;
-    @Mock private SecretKey mTertiaryKey;
-    @Mock private SecureRandom mSecureRandom;
-
-    private EncryptedFullBackupTask mTask;
-    private ChunkListing mOldChunkListing;
-    private ChunkListing mNewChunkListing;
-    private WrappedKey mWrappedTertiaryKey;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mWrappedTertiaryKey = new WrappedKey();
-        when(mTertiaryKeyManager.getKey()).thenReturn(mTertiaryKey);
-        when(mTertiaryKeyManager.getWrappedKey()).thenReturn(mWrappedTertiaryKey);
-
-        mOldChunkListing =
-                CryptoTestUtils.newChunkListing(
-                        /* docId */ null,
-                        TEST_EXISTING_FINGERPRINT_MIXER_SALT,
-                        ChunksMetadataProto.AES_256_GCM,
-                        ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED,
-                        CryptoTestUtils.newChunk(TEST_CHUNK_HASH_1.getHash(), TEST_CHUNK_LENGTH_1));
-        mNewChunkListing =
-                CryptoTestUtils.newChunkListing(
-                        /* docId */ null,
-                        /* fingerprintSalt */ null,
-                        ChunksMetadataProto.AES_256_GCM,
-                        ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED,
-                        CryptoTestUtils.newChunk(TEST_CHUNK_HASH_1.getHash(), TEST_CHUNK_LENGTH_1),
-                        CryptoTestUtils.newChunk(TEST_CHUNK_HASH_2.getHash(), TEST_CHUNK_LENGTH_2));
-        when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
-                .thenReturn(mNewChunkListing);
-        when(mEncryptedBackupTask.performIncrementalBackup(any(), any(), any()))
-                .thenReturn(mNewChunkListing);
-        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
-
-        doAnswer(invocation -> {
-            byte[] byteArray = (byte[]) invocation.getArguments()[0];
-            System.arraycopy(
-                    TEST_GENERATED_FINGERPRINT_MIXER_SALT,
-                    /* srcPos */ 0,
-                    byteArray,
-                    /* destPos */ 0,
-                    FingerprintMixer.SALT_LENGTH_BYTES);
-            return null;
-        })
-                .when(mSecureRandom)
-                .nextBytes(any(byte[].class));
-
-        mTask =
-                new EncryptedFullBackupTask(
-                        mChunkListingStore,
-                        mTertiaryKeyManager,
-                        mEncryptedBackupTask,
-                        mInputStream,
-                        TEST_PACKAGE_NAME,
-                        mSecureRandom);
-    }
-
-    @Test
-    public void call_existingChunkListingButTertiaryKeyRotated_performsNonIncrementalBackup()
-            throws Exception {
-        when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(true);
-        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
-                .thenReturn(Optional.of(mOldChunkListing));
-
-        mTask.call();
-
-        verify(mEncryptedBackupTask)
-                .performNonIncrementalBackup(
-                        eq(mTertiaryKey),
-                        eq(mWrappedTertiaryKey),
-                        eq(TEST_GENERATED_FINGERPRINT_MIXER_SALT));
-    }
-
-    @Test
-    public void call_noExistingChunkListing_performsNonIncrementalBackup() throws Exception {
-        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
-        mTask.call();
-        verify(mEncryptedBackupTask)
-                .performNonIncrementalBackup(
-                        eq(mTertiaryKey),
-                        eq(mWrappedTertiaryKey),
-                        eq(TEST_GENERATED_FINGERPRINT_MIXER_SALT));
-    }
-
-    @Test
-    public void call_existingChunkListing_performsIncrementalBackup() throws Exception {
-        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
-                .thenReturn(Optional.of(mOldChunkListing));
-        mTask.call();
-        verify(mEncryptedBackupTask)
-                .performIncrementalBackup(
-                        eq(mTertiaryKey), eq(mWrappedTertiaryKey), eq(mOldChunkListing));
-    }
-
-    @Test
-    public void
-            call_existingChunkListingWithNoFingerprintMixerSalt_doesntSetSaltBeforeIncBackup()
-                    throws Exception {
-        mOldChunkListing.fingerprintMixerSalt = new byte[0];
-        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
-                .thenReturn(Optional.of(mOldChunkListing));
-
-        mTask.call();
-
-        verify(mEncryptedBackupTask)
-                .performIncrementalBackup(
-                        eq(mTertiaryKey), eq(mWrappedTertiaryKey), eq(mOldChunkListing));
-    }
-
-    @Test
-    public void call_noExistingChunkListing_storesNewChunkListing() throws Exception {
-        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
-        mTask.call();
-        verify(mChunkListingStore).saveProto(TEST_PACKAGE_NAME, mNewChunkListing);
-    }
-
-    @Test
-    public void call_existingChunkListing_storesNewChunkListing() throws Exception {
-        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
-                .thenReturn(Optional.of(mOldChunkListing));
-        mTask.call();
-        verify(mChunkListingStore).saveProto(TEST_PACKAGE_NAME, mNewChunkListing);
-    }
-
-    @Test
-    public void call_exceptionDuringBackup_doesNotSaveNewChunkListing() throws Exception {
-        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
-        when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
-                .thenThrow(GeneralSecurityException.class);
-
-        assertThrows(Exception.class, () -> mTask.call());
-
-        assertThat(mChunkListingStore.loadProto(TEST_PACKAGE_NAME).isPresent()).isFalse();
-    }
-
-    @Test
-    public void call_incrementalThrowsPermanentException_clearsState() throws Exception {
-        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
-                .thenReturn(Optional.of(mOldChunkListing));
-        when(mEncryptedBackupTask.performIncrementalBackup(any(), any(), any()))
-                .thenThrow(IOException.class);
-
-        assertThrows(IOException.class, () -> mTask.call());
-
-        verify(mChunkListingStore).deleteProto(TEST_PACKAGE_NAME);
-    }
-
-    @Test
-    public void call_closesInputStream() throws Exception {
-        mTask.call();
-        verify(mInputStream).close();
-    }
-
-    @Test
-    public void cancel_cancelsTask() throws Exception {
-        mTask.cancel();
-        verify(mEncryptedBackupTask).cancel();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTaskTest.java
deleted file mode 100644
index 0affacd..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTaskTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-
-import static java.util.stream.Collectors.toList;
-
-import com.android.server.backup.encryption.FullRestoreDownloader;
-
-import com.google.common.io.Files;
-import com.google.common.primitives.Bytes;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-
-@RunWith(RobolectricTestRunner.class)
-public class EncryptedFullRestoreTaskTest {
-    private static final int TEST_BUFFER_SIZE = 10;
-    private static final byte[] TEST_ENCRYPTED_DATA = {1, 2, 3, 4, 5, 6};
-    private static final byte[] TEST_DECRYPTED_DATA = fakeDecrypt(TEST_ENCRYPTED_DATA);
-
-    @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
-    @Mock private BackupFileDecryptorTask mDecryptorTask;
-
-    private File mFolder;
-    private FakeFullRestoreDownloader mFullRestorePackageWrapper;
-    private EncryptedFullRestoreTask mTask;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mFolder = temporaryFolder.newFolder();
-        mFullRestorePackageWrapper = new FakeFullRestoreDownloader(TEST_ENCRYPTED_DATA);
-
-        doAnswer(
-            invocation -> {
-                File source = invocation.getArgument(0);
-                DecryptedChunkOutput target = invocation.getArgument(1);
-                byte[] decrypted = fakeDecrypt(Files.toByteArray(source));
-                target.open();
-                target.processChunk(decrypted, decrypted.length);
-                target.close();
-                return null;
-            })
-                .when(mDecryptorTask)
-                .decryptFile(any(), any());
-
-        mTask = new EncryptedFullRestoreTask(mFolder, mFullRestorePackageWrapper, mDecryptorTask);
-    }
-
-    @Test
-    public void readNextChunk_downloadsAndDecryptsBackup() throws Exception {
-        ByteArrayOutputStream decryptedOutput = new ByteArrayOutputStream();
-
-        byte[] buffer = new byte[TEST_BUFFER_SIZE];
-        int bytesRead = mTask.readNextChunk(buffer);
-        while (bytesRead != -1) {
-            decryptedOutput.write(buffer, 0, bytesRead);
-            bytesRead = mTask.readNextChunk(buffer);
-        }
-
-        assertThat(decryptedOutput.toByteArray()).isEqualTo(TEST_DECRYPTED_DATA);
-    }
-
-    @Test
-    public void finish_deletesTemporaryFiles() throws Exception {
-        mTask.readNextChunk(new byte[10]);
-        mTask.finish(FullRestoreDownloader.FinishType.UNKNOWN_FINISH);
-
-        assertThat(mFolder.listFiles()).isEmpty();
-    }
-
-    /** Fake package wrapper which returns data from a byte array. */
-    private static class FakeFullRestoreDownloader extends FullRestoreDownloader {
-        private final ByteArrayInputStream mData;
-
-        FakeFullRestoreDownloader(byte[] data) {
-            // We override all methods of the superclass, so it does not require any collaborators.
-            super();
-            mData = new ByteArrayInputStream(data);
-        }
-
-        @Override
-        public int readNextChunk(byte[] buffer) throws IOException {
-            return mData.read(buffer);
-        }
-
-        @Override
-        public void finish(FinishType finishType) {
-            // Nothing to do.
-        }
-    }
-
-    /** Fake decrypts a byte array by subtracting 1 from each byte. */
-    private static byte[] fakeDecrypt(byte[] input) {
-        return Bytes.toArray(Bytes.asList(input).stream().map(b -> b + 1).collect(toList()));
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java
deleted file mode 100644
index 222b882..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertThrows;
-
-import android.app.Application;
-import android.util.Pair;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.ProtoStore;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.TertiaryKeyManager;
-import com.android.server.backup.encryption.kv.KeyValueListingBuilder;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-import com.android.server.backup.testing.CryptoTestUtils;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.crypto.SecretKey;
-
-
-@RunWith(RobolectricTestRunner.class)
-public class EncryptedKvBackupTaskTest {
-    private static final boolean INCREMENTAL = true;
-    private static final boolean NON_INCREMENTAL = false;
-
-    private static final String TEST_PACKAGE_1 = "com.example.app1";
-    private static final String TEST_KEY_1 = "key_1";
-    private static final String TEST_KEY_2 = "key_2";
-    private static final ChunkHash TEST_HASH_1 =
-            new ChunkHash(Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES));
-    private static final ChunkHash TEST_HASH_2 =
-            new ChunkHash(Arrays.copyOf(new byte[] {2}, ChunkHash.HASH_LENGTH_BYTES));
-    private static final int TEST_LENGTH_1 = 200;
-    private static final int TEST_LENGTH_2 = 300;
-
-    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
-    @Captor private ArgumentCaptor<ChunksMetadataProto.ChunkListing> mChunkListingCaptor;
-
-    @Mock private TertiaryKeyManager mTertiaryKeyManager;
-    @Mock private RecoverableKeyStoreSecondaryKey mSecondaryKey;
-    @Mock private ProtoStore<KeyValueListingProto.KeyValueListing> mKeyValueListingStore;
-    @Mock private ProtoStore<ChunksMetadataProto.ChunkListing> mChunkListingStore;
-    @Mock private KvBackupEncrypter mKvBackupEncrypter;
-    @Mock private EncryptedBackupTask mEncryptedBackupTask;
-    @Mock private SecretKey mTertiaryKey;
-
-    private WrappedKeyProto.WrappedKey mWrappedTertiaryKey;
-    private KeyValueListingProto.KeyValueListing mNewKeyValueListing;
-    private ChunksMetadataProto.ChunkListing mNewChunkListing;
-    private EncryptedKvBackupTask mTask;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        Application application = ApplicationProvider.getApplicationContext();
-        mKeyValueListingStore = ProtoStore.createKeyValueListingStore(application);
-        mChunkListingStore = ProtoStore.createChunkListingStore(application);
-
-        mWrappedTertiaryKey = new WrappedKeyProto.WrappedKey();
-
-        when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(false);
-        when(mTertiaryKeyManager.getKey()).thenReturn(mTertiaryKey);
-        when(mTertiaryKeyManager.getWrappedKey()).thenReturn(mWrappedTertiaryKey);
-
-        mNewKeyValueListing =
-                createKeyValueListing(
-                        CryptoTestUtils.mapOf(
-                                new Pair<>(TEST_KEY_1, TEST_HASH_1),
-                                new Pair<>(TEST_KEY_2, TEST_HASH_2)));
-        mNewChunkListing =
-                createChunkListing(
-                        CryptoTestUtils.mapOf(
-                                new Pair<>(TEST_HASH_1, TEST_LENGTH_1),
-                                new Pair<>(TEST_HASH_2, TEST_LENGTH_2)));
-        when(mKvBackupEncrypter.getNewKeyValueListing()).thenReturn(mNewKeyValueListing);
-        when(mEncryptedBackupTask.performIncrementalBackup(
-                        eq(mTertiaryKey), eq(mWrappedTertiaryKey), any()))
-                .thenReturn(mNewChunkListing);
-        when(mEncryptedBackupTask.performNonIncrementalBackup(
-                        eq(mTertiaryKey), eq(mWrappedTertiaryKey), any()))
-                .thenReturn(mNewChunkListing);
-
-        mTask =
-                new EncryptedKvBackupTask(
-                        mTertiaryKeyManager,
-                        mKeyValueListingStore,
-                        mSecondaryKey,
-                        mChunkListingStore,
-                        mKvBackupEncrypter,
-                        mEncryptedBackupTask,
-                        TEST_PACKAGE_1);
-    }
-
-    @Test
-    public void testPerformBackup_rotationRequired_deletesListings() throws Exception {
-        mKeyValueListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
-        mChunkListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
-
-        when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(true);
-        // Throw an IOException so it aborts before saving the new listings.
-        when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
-                .thenThrow(IOException.class);
-
-        assertThrows(IOException.class, () -> mTask.performBackup(NON_INCREMENTAL));
-
-        assertFalse(mKeyValueListingStore.loadProto(TEST_PACKAGE_1).isPresent());
-        assertFalse(mChunkListingStore.loadProto(TEST_PACKAGE_1).isPresent());
-    }
-
-    @Test
-    public void testPerformBackup_rotationRequiredButIncremental_throws() throws Exception {
-        mKeyValueListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
-        mChunkListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
-
-        when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(true);
-
-        assertThrows(NonIncrementalBackupRequiredException.class,
-                () -> mTask.performBackup(INCREMENTAL));
-    }
-
-    @Test
-    public void testPerformBackup_rotationRequiredAndNonIncremental_performsNonIncrementalBackup()
-            throws Exception {
-        mKeyValueListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
-        mChunkListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
-
-        when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(true);
-
-        mTask.performBackup(NON_INCREMENTAL);
-
-        verify(mEncryptedBackupTask)
-                .performNonIncrementalBackup(eq(mTertiaryKey), eq(mWrappedTertiaryKey), any());
-    }
-
-    @Test
-    public void testPerformBackup_existingStateButNonIncremental_deletesListings() throws Exception {
-        mKeyValueListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
-        mChunkListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
-
-        // Throw an IOException so it aborts before saving the new listings.
-        when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
-                .thenThrow(IOException.class);
-
-        assertThrows(IOException.class, () -> mTask.performBackup(NON_INCREMENTAL));
-
-        assertFalse(mKeyValueListingStore.loadProto(TEST_PACKAGE_1).isPresent());
-        assertFalse(mChunkListingStore.loadProto(TEST_PACKAGE_1).isPresent());
-    }
-
-    @Test
-    public void testPerformBackup_keyValueListingMissing_deletesChunkListingAndPerformsNonIncremental()
-            throws Exception {
-        mChunkListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
-
-        // Throw an IOException so it aborts before saving the new listings.
-        when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
-                .thenThrow(IOException.class);
-
-        assertThrows(IOException.class, () -> mTask.performBackup(NON_INCREMENTAL));
-
-        verify(mEncryptedBackupTask).performNonIncrementalBackup(any(), any(), any());
-        assertFalse(mKeyValueListingStore.loadProto(TEST_PACKAGE_1).isPresent());
-        assertFalse(mChunkListingStore.loadProto(TEST_PACKAGE_1).isPresent());
-    }
-
-    @Test
-    public void testPerformBackup_chunkListingMissing_deletesKeyValueListingAndPerformsNonIncremental()
-            throws Exception {
-        mKeyValueListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
-
-        // Throw an IOException so it aborts before saving the new listings.
-        when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
-                .thenThrow(IOException.class);
-
-        assertThrows(IOException.class, () -> mTask.performBackup(NON_INCREMENTAL));
-
-        verify(mEncryptedBackupTask).performNonIncrementalBackup(any(), any(), any());
-        assertFalse(mKeyValueListingStore.loadProto(TEST_PACKAGE_1).isPresent());
-        assertFalse(mChunkListingStore.loadProto(TEST_PACKAGE_1).isPresent());
-    }
-
-    @Test
-    public void testPerformBackup_existingStateAndIncremental_performsIncrementalBackup()
-            throws Exception {
-        mKeyValueListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
-        ChunksMetadataProto.ChunkListing oldChunkListing =
-                createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1)));
-        mChunkListingStore.saveProto(TEST_PACKAGE_1, oldChunkListing);
-
-        mTask.performBackup(INCREMENTAL);
-
-        verify(mEncryptedBackupTask)
-                .performIncrementalBackup(
-                        eq(mTertiaryKey), eq(mWrappedTertiaryKey), mChunkListingCaptor.capture());
-        assertChunkListingsEqual(mChunkListingCaptor.getValue(), oldChunkListing);
-    }
-
-    @Test
-    public void testPerformBackup_noExistingStateAndNonIncremental_performsNonIncrementalBackup()
-            throws Exception {
-        mTask.performBackup(NON_INCREMENTAL);
-
-        verify(mEncryptedBackupTask)
-                .performNonIncrementalBackup(eq(mTertiaryKey), eq(mWrappedTertiaryKey), eq(null));
-    }
-
-    @Test
-    public void testPerformBackup_incremental_savesNewListings() throws Exception {
-        mKeyValueListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
-        mChunkListingStore.saveProto(
-                TEST_PACKAGE_1,
-                createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
-
-        mTask.performBackup(INCREMENTAL);
-
-        KeyValueListingProto.KeyValueListing actualKeyValueListing =
-                mKeyValueListingStore.loadProto(TEST_PACKAGE_1).get();
-        ChunksMetadataProto.ChunkListing actualChunkListing =
-                mChunkListingStore.loadProto(TEST_PACKAGE_1).get();
-        assertKeyValueListingsEqual(actualKeyValueListing, mNewKeyValueListing);
-        assertChunkListingsEqual(actualChunkListing, mNewChunkListing);
-    }
-
-    @Test
-    public void testPerformBackup_nonIncremental_savesNewListings() throws Exception {
-        mTask.performBackup(NON_INCREMENTAL);
-
-        KeyValueListingProto.KeyValueListing actualKeyValueListing =
-                mKeyValueListingStore.loadProto(TEST_PACKAGE_1).get();
-        ChunksMetadataProto.ChunkListing actualChunkListing =
-                mChunkListingStore.loadProto(TEST_PACKAGE_1).get();
-        assertKeyValueListingsEqual(actualKeyValueListing, mNewKeyValueListing);
-        assertChunkListingsEqual(actualChunkListing, mNewChunkListing);
-    }
-
-    private static KeyValueListingProto.KeyValueListing createKeyValueListing(
-            Map<String, ChunkHash> pairs) {
-        return new KeyValueListingBuilder().addAll(pairs).build();
-    }
-
-    private static ChunksMetadataProto.ChunkListing createChunkListing(
-            Map<ChunkHash, Integer> chunks) {
-        ChunksMetadataProto.Chunk[] listingChunks = new ChunksMetadataProto.Chunk[chunks.size()];
-        int chunksAdded = 0;
-        for (Entry<ChunkHash, Integer> entry : chunks.entrySet()) {
-            listingChunks[chunksAdded] = CryptoTestUtils.newChunk(entry.getKey(), entry.getValue());
-            chunksAdded++;
-        }
-        return CryptoTestUtils.newChunkListingWithoutDocId(
-                /* fingerprintSalt */ new byte[0],
-                ChunksMetadataProto.AES_256_GCM,
-                ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED,
-                listingChunks);
-    }
-
-    private static void assertKeyValueListingsEqual(
-            KeyValueListingProto.KeyValueListing actual,
-            KeyValueListingProto.KeyValueListing expected) {
-        KeyValueListingProto.KeyValueEntry[] actualEntries = actual.entries;
-        KeyValueListingProto.KeyValueEntry[] expectedEntries = expected.entries;
-        assertThat(actualEntries.length).isEqualTo(expectedEntries.length);
-        for (int i = 0; i < actualEntries.length; i++) {
-            assertWithMessage("entry " + i)
-                    .that(actualEntries[i].key)
-                    .isEqualTo(expectedEntries[i].key);
-            assertWithMessage("entry " + i)
-                    .that(actualEntries[i].hash)
-                    .isEqualTo(expectedEntries[i].hash);
-        }
-    }
-
-    private static void assertChunkListingsEqual(
-            ChunksMetadataProto.ChunkListing actual, ChunksMetadataProto.ChunkListing expected) {
-        ChunksMetadataProto.Chunk[] actualChunks = actual.chunks;
-        ChunksMetadataProto.Chunk[] expectedChunks = expected.chunks;
-        assertThat(actualChunks.length).isEqualTo(expectedChunks.length);
-        for (int i = 0; i < actualChunks.length; i++) {
-            assertWithMessage("chunk " + i)
-                    .that(actualChunks[i].hash)
-                    .isEqualTo(expectedChunks[i].hash);
-            assertWithMessage("chunk " + i)
-                    .that(actualChunks[i].length)
-                    .isEqualTo(expectedChunks[i].length);
-        }
-        assertThat(actual.cipherType).isEqualTo(expected.cipherType);
-        assertThat(actual.documentId)
-                .isEqualTo(expected.documentId == null ? "" : expected.documentId);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTaskTest.java
deleted file mode 100644
index 6666d95..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTaskTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-
-import android.os.ParcelFileDescriptor;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.ChunkHasher;
-import com.android.server.backup.testing.CryptoTestUtils;
-import com.android.server.testing.shadows.DataEntity;
-import com.android.server.testing.shadows.ShadowBackupDataOutput;
-
-import com.google.protobuf.nano.MessageNano;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@Config(shadows = {ShadowBackupDataOutput.class})
-@RunWith(RobolectricTestRunner.class)
-public class EncryptedKvRestoreTaskTest {
-    private static final String TEST_KEY_1 = "test_key_1";
-    private static final String TEST_KEY_2 = "test_key_2";
-    private static final String TEST_KEY_3 = "test_key_3";
-    private static final byte[] TEST_VALUE_1 = {1, 2, 3};
-    private static final byte[] TEST_VALUE_2 = {4, 5, 6};
-    private static final byte[] TEST_VALUE_3 = {20, 25, 30, 35};
-
-    @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
-    private File temporaryDirectory;
-
-    @Mock private ParcelFileDescriptor mParcelFileDescriptor;
-    @Mock private ChunkHasher mChunkHasher;
-    @Mock private FullRestoreToFileTask mFullRestoreToFileTask;
-    @Mock private BackupFileDecryptorTask mBackupFileDecryptorTask;
-
-    private EncryptedKvRestoreTask task;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        when(mChunkHasher.computeHash(any()))
-                .thenAnswer(invocation -> fakeHash(invocation.getArgument(0)));
-        doAnswer(invocation -> writeTestPairsToFile(invocation.getArgument(0)))
-                .when(mFullRestoreToFileTask)
-                .restoreToFile(any());
-        doAnswer(
-                        invocation ->
-                                readPairsFromFile(
-                                        invocation.getArgument(0), invocation.getArgument(1)))
-                .when(mBackupFileDecryptorTask)
-                .decryptFile(any(), any());
-
-        temporaryDirectory = temporaryFolder.newFolder();
-        task =
-                new EncryptedKvRestoreTask(
-                        temporaryDirectory,
-                        mChunkHasher,
-                        mFullRestoreToFileTask,
-                        mBackupFileDecryptorTask);
-    }
-
-    @Test
-    public void testGetRestoreData_writesPairsToOutputInOrder() throws Exception {
-        task.getRestoreData(mParcelFileDescriptor);
-
-        assertThat(ShadowBackupDataOutput.getEntities())
-                .containsExactly(
-                        new DataEntity(TEST_KEY_1, TEST_VALUE_1),
-                        new DataEntity(TEST_KEY_2, TEST_VALUE_2),
-                        new DataEntity(TEST_KEY_3, TEST_VALUE_3))
-                .inOrder();
-    }
-
-    @Test
-    public void testGetRestoreData_exceptionDuringDecryption_throws() throws Exception {
-        doThrow(IOException.class).when(mBackupFileDecryptorTask).decryptFile(any(), any());
-        assertThrows(IOException.class, () -> task.getRestoreData(mParcelFileDescriptor));
-    }
-
-    @Test
-    public void testGetRestoreData_exceptionDuringDownload_throws() throws Exception {
-        doThrow(IOException.class).when(mFullRestoreToFileTask).restoreToFile(any());
-        assertThrows(IOException.class, () -> task.getRestoreData(mParcelFileDescriptor));
-    }
-
-    @Test
-    public void testGetRestoreData_exceptionDuringDecryption_deletesTemporaryFiles() throws Exception {
-        doThrow(InvalidKeyException.class).when(mBackupFileDecryptorTask).decryptFile(any(), any());
-        assertThrows(InvalidKeyException.class, () -> task.getRestoreData(mParcelFileDescriptor));
-        assertThat(temporaryDirectory.listFiles()).isEmpty();
-    }
-
-    @Test
-    public void testGetRestoreData_exceptionDuringDownload_deletesTemporaryFiles() throws Exception {
-        doThrow(IOException.class).when(mFullRestoreToFileTask).restoreToFile(any());
-        assertThrows(IOException.class, () -> task.getRestoreData(mParcelFileDescriptor));
-        assertThat(temporaryDirectory.listFiles()).isEmpty();
-    }
-
-    private static Void writeTestPairsToFile(File file) throws IOException {
-        // Write the pairs out of order to check the task sorts them.
-        Set<byte[]> pairs =
-                new HashSet<>(
-                        Arrays.asList(
-                                createPair(TEST_KEY_1, TEST_VALUE_1),
-                                createPair(TEST_KEY_3, TEST_VALUE_3),
-                                createPair(TEST_KEY_2, TEST_VALUE_2)));
-
-        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
-            oos.writeObject(pairs);
-        }
-        return null;
-    }
-
-    private static Void readPairsFromFile(File file, DecryptedChunkOutput decryptedChunkOutput)
-            throws IOException, ClassNotFoundException, InvalidKeyException,
-                    NoSuchAlgorithmException {
-        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
-                DecryptedChunkOutput output = decryptedChunkOutput.open()) {
-            Set<byte[]> pairs = readPairs(ois);
-            for (byte[] pair : pairs) {
-                output.processChunk(pair, pair.length);
-            }
-        }
-
-        return null;
-    }
-
-    private static byte[] createPair(String key, byte[] value) {
-        return MessageNano.toByteArray(CryptoTestUtils.newPair(key, value));
-    }
-
-    @SuppressWarnings("unchecked") // deserialization.
-    private static Set<byte[]> readPairs(ObjectInputStream ois)
-            throws IOException, ClassNotFoundException {
-        return (Set<byte[]>) ois.readObject();
-    }
-
-    private static ChunkHash fakeHash(byte[] data) {
-        return new ChunkHash(Arrays.copyOf(data, ChunkHash.HASH_LENGTH_BYTES));
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTaskTest.java
deleted file mode 100644
index de8b734..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTaskTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.encryption.FullRestoreDownloader;
-import com.android.server.backup.encryption.FullRestoreDownloader.FinishType;
-
-import com.google.common.io.Files;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.util.Random;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class FullRestoreToFileTaskTest {
-    private static final int TEST_RANDOM_SEED = 34;
-    private static final int TEST_MAX_CHUNK_SIZE_BYTES = 5;
-    private static final int TEST_DATA_LENGTH_BYTES = TEST_MAX_CHUNK_SIZE_BYTES * 20;
-
-    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
-    private byte[] mTestData;
-    private File mTargetFile;
-    private FakeFullRestoreDownloader mFakeFullRestoreDownloader;
-    @Mock private FullRestoreDownloader mMockFullRestoreDownloader;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mTargetFile = mTemporaryFolder.newFile();
-
-        mTestData = new byte[TEST_DATA_LENGTH_BYTES];
-        new Random(TEST_RANDOM_SEED).nextBytes(mTestData);
-        mFakeFullRestoreDownloader = new FakeFullRestoreDownloader(mTestData);
-    }
-
-    private FullRestoreToFileTask createTaskWithFakeDownloader() {
-        return new FullRestoreToFileTask(mFakeFullRestoreDownloader, TEST_MAX_CHUNK_SIZE_BYTES);
-    }
-
-    private FullRestoreToFileTask createTaskWithMockDownloader() {
-        return new FullRestoreToFileTask(mMockFullRestoreDownloader, TEST_MAX_CHUNK_SIZE_BYTES);
-    }
-
-    @Test
-    public void restoreToFile_readsDataAndWritesToFile() throws Exception {
-        FullRestoreToFileTask task = createTaskWithFakeDownloader();
-        task.restoreToFile(mTargetFile);
-        assertThat(Files.toByteArray(mTargetFile)).isEqualTo(mTestData);
-    }
-
-    @Test
-    public void restoreToFile_noErrors_closesDownloaderWithFinished() throws Exception {
-        FullRestoreToFileTask task = createTaskWithMockDownloader();
-        when(mMockFullRestoreDownloader.readNextChunk(any())).thenReturn(-1);
-
-        task.restoreToFile(mTargetFile);
-
-        verify(mMockFullRestoreDownloader).finish(FinishType.FINISHED);
-    }
-
-    @Test
-    public void restoreToFile_ioException_closesDownloaderWithTransferFailure() throws Exception {
-        FullRestoreToFileTask task = createTaskWithMockDownloader();
-        when(mMockFullRestoreDownloader.readNextChunk(any())).thenThrow(IOException.class);
-
-        assertThrows(IOException.class, () -> task.restoreToFile(mTargetFile));
-
-        verify(mMockFullRestoreDownloader).finish(FinishType.TRANSFER_FAILURE);
-    }
-
-    /** Fake package wrapper which returns data from a byte array. */
-    private static class FakeFullRestoreDownloader extends FullRestoreDownloader {
-
-        private final ByteArrayInputStream mData;
-
-        FakeFullRestoreDownloader(byte[] data) {
-            // We override all methods of the superclass, so it does not require any collaborators.
-            super();
-            this.mData = new ByteArrayInputStream(data);
-        }
-
-        @Override
-        public int readNextChunk(byte[] buffer) throws IOException {
-            return mData.read(buffer);
-        }
-
-        @Override
-        public void finish(FinishType finishType) {
-            // Do nothing.
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTaskTest.java
deleted file mode 100644
index 4a7ae03..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTaskTest.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static android.security.keystore.recovery.RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-
-import android.app.Application;
-import android.security.keystore.recovery.RecoveryController;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
-import com.android.server.testing.fakes.FakeCryptoBackupServer;
-import com.android.server.testing.shadows.ShadowRecoveryController;
-
-import java.security.InvalidKeyException;
-import java.security.SecureRandom;
-import java.util.Optional;
-
-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.annotation.Config;
-
-@Config(shadows = {ShadowRecoveryController.class})
-@RunWith(RobolectricTestRunner.class)
-public class InitializeRecoverableSecondaryKeyTaskTest {
-    @Mock private CryptoSettings mMockCryptoSettings;
-
-    private Application mApplication;
-    private InitializeRecoverableSecondaryKeyTask mTask;
-    private CryptoSettings mCryptoSettings;
-    private FakeCryptoBackupServer mFakeCryptoBackupServer;
-    private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        ShadowRecoveryController.reset();
-
-        mApplication = ApplicationProvider.getApplicationContext();
-        mFakeCryptoBackupServer = new FakeCryptoBackupServer();
-        mCryptoSettings = CryptoSettings.getInstanceForTesting(mApplication);
-        mSecondaryKeyManager =
-                new RecoverableKeyStoreSecondaryKeyManager(
-                        RecoveryController.getInstance(mApplication), new SecureRandom());
-
-        mTask =
-                new InitializeRecoverableSecondaryKeyTask(
-                        mApplication, mCryptoSettings, mSecondaryKeyManager, mFakeCryptoBackupServer);
-    }
-
-    @Test
-    public void testRun_generatesNewKeyInRecoveryController() throws Exception {
-        RecoverableKeyStoreSecondaryKey key = mTask.run();
-
-        assertThat(RecoveryController.getInstance(mApplication).getAliases())
-                .contains(key.getAlias());
-    }
-
-    @Test
-    public void testRun_setsAliasOnServer() throws Exception {
-        RecoverableKeyStoreSecondaryKey key = mTask.run();
-
-        assertThat(mFakeCryptoBackupServer.getActiveSecondaryKeyAlias().get())
-                .isEqualTo(key.getAlias());
-    }
-
-    @Test
-    public void testRun_setsAliasInSettings() throws Exception {
-        RecoverableKeyStoreSecondaryKey key = mTask.run();
-
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get()).isEqualTo(key.getAlias());
-    }
-
-    @Test
-    public void testRun_initializesSettings() throws Exception {
-        mTask.run();
-
-        assertThat(mCryptoSettings.getIsInitialized()).isTrue();
-    }
-
-    @Test
-    public void testRun_initializeSettingsFails_throws() throws Exception {
-        useMockCryptoSettings();
-        doThrow(IllegalArgumentException.class)
-                .when(mMockCryptoSettings)
-                .initializeWithKeyAlias(any());
-
-
-        assertThrows(IllegalArgumentException.class, () -> mTask.run());
-    }
-
-    @Test
-    public void testRun_doesNotGenerateANewKeyIfOneIsAvailable() throws Exception {
-        RecoverableKeyStoreSecondaryKey key1 = mTask.run();
-        RecoverableKeyStoreSecondaryKey key2 = mTask.run();
-
-        assertThat(key1.getAlias()).isEqualTo(key2.getAlias());
-        assertThat(key2.getSecretKey()).isEqualTo(key2.getSecretKey());
-    }
-
-    @Test
-    public void testRun_existingKeyButDestroyed_throws() throws Exception {
-        RecoverableKeyStoreSecondaryKey key = mTask.run();
-        ShadowRecoveryController.setRecoveryStatus(
-                key.getAlias(), RECOVERY_STATUS_PERMANENT_FAILURE);
-
-        assertThrows(InvalidKeyException.class, () -> mTask.run());
-    }
-
-    @Test
-    public void testRun_settingsInitializedButNotSecondaryKeyAlias_throws() {
-        useMockCryptoSettings();
-        when(mMockCryptoSettings.getIsInitialized()).thenReturn(true);
-        when(mMockCryptoSettings.getActiveSecondaryKeyAlias()).thenReturn(Optional.empty());
-
-        assertThrows(InvalidKeyException.class, () -> mTask.run());
-    }
-
-    @Test
-    public void testRun_keyAliasSetButNotInStore_throws() {
-        useMockCryptoSettings();
-        when(mMockCryptoSettings.getIsInitialized()).thenReturn(true);
-        when(mMockCryptoSettings.getActiveSecondaryKeyAlias())
-                .thenReturn(Optional.of("missingAlias"));
-
-        assertThrows(InvalidKeyException.class, () -> mTask.run());
-    }
-
-    private void useMockCryptoSettings() {
-        mTask =
-                new InitializeRecoverableSecondaryKeyTask(
-                        mApplication,
-                        mMockCryptoSettings,
-                        mSecondaryKeyManager,
-                        mFakeCryptoBackupServer);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/KvBackupEncrypterTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/KvBackupEncrypterTest.java
deleted file mode 100644
index ccfbfa4..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/KvBackupEncrypterTest.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.app.backup.BackupDataInput;
-import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunking.ChunkHasher;
-import com.android.server.backup.encryption.chunking.EncryptedChunk;
-import com.android.server.backup.encryption.kv.KeyValueListingBuilder;
-import com.android.server.backup.encryption.protos.nano.KeyValueListingProto.KeyValueListing;
-import com.android.server.backup.encryption.protos.nano.KeyValuePairProto.KeyValuePair;
-import com.android.server.backup.encryption.tasks.BackupEncrypter.Result;
-import com.android.server.testing.shadows.DataEntity;
-import com.android.server.testing.shadows.ShadowBackupDataInput;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Ordering;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-import java.security.MessageDigest;
-import java.util.Arrays;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-@Config(shadows = {ShadowBackupDataInput.class})
-public class KvBackupEncrypterTest {
-    private static final String KEY_ALGORITHM = "AES";
-    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
-    private static final int GCM_TAG_LENGTH_BYTES = 16;
-
-    private static final byte[] TEST_TERTIARY_KEY = Arrays.copyOf(new byte[0], 256 / Byte.SIZE);
-    private static final String TEST_KEY_1 = "test_key_1";
-    private static final String TEST_KEY_2 = "test_key_2";
-    private static final String TEST_KEY_3 = "test_key_3";
-    private static final byte[] TEST_VALUE_1 = {10, 11, 12};
-    private static final byte[] TEST_VALUE_2 = {13, 14, 15};
-    private static final byte[] TEST_VALUE_2B = {13, 14, 15, 16};
-    private static final byte[] TEST_VALUE_3 = {16, 17, 18};
-
-    private SecretKey mSecretKey;
-    private ChunkHasher mChunkHasher;
-
-    @Before
-    public void setUp() {
-        mSecretKey = new SecretKeySpec(TEST_TERTIARY_KEY, KEY_ALGORITHM);
-        mChunkHasher = new ChunkHasher(mSecretKey);
-
-        ShadowBackupDataInput.reset();
-    }
-
-    private KvBackupEncrypter createEncrypter(KeyValueListing keyValueListing) {
-        KvBackupEncrypter encrypter = new KvBackupEncrypter(new BackupDataInput(null));
-        encrypter.setOldKeyValueListing(keyValueListing);
-        return encrypter;
-    }
-
-    @Test
-    public void backup_noExistingBackup_encryptsAllPairs() throws Exception {
-        ShadowBackupDataInput.addEntity(TEST_KEY_1, TEST_VALUE_1);
-        ShadowBackupDataInput.addEntity(TEST_KEY_2, TEST_VALUE_2);
-
-        KeyValueListing emptyKeyValueListing = new KeyValueListingBuilder().build();
-        ImmutableSet<ChunkHash> emptyExistingChunks = ImmutableSet.of();
-        KvBackupEncrypter encrypter = createEncrypter(emptyKeyValueListing);
-
-        Result result =
-                encrypter.backup(
-                        mSecretKey, /*unusedFingerprintMixerSalt=*/ null, emptyExistingChunks);
-
-        assertThat(result.getAllChunks()).hasSize(2);
-        EncryptedChunk chunk1 = result.getNewChunks().get(0);
-        EncryptedChunk chunk2 = result.getNewChunks().get(1);
-        assertThat(chunk1.key()).isEqualTo(getChunkHash(TEST_KEY_1, TEST_VALUE_1));
-        KeyValuePair pair1 = decryptChunk(chunk1);
-        assertThat(pair1.key).isEqualTo(TEST_KEY_1);
-        assertThat(pair1.value).isEqualTo(TEST_VALUE_1);
-        assertThat(chunk2.key()).isEqualTo(getChunkHash(TEST_KEY_2, TEST_VALUE_2));
-        KeyValuePair pair2 = decryptChunk(chunk2);
-        assertThat(pair2.key).isEqualTo(TEST_KEY_2);
-        assertThat(pair2.value).isEqualTo(TEST_VALUE_2);
-    }
-
-    @Test
-    public void backup_existingBackup_encryptsNewAndUpdatedPairs() throws Exception {
-        Pair<KeyValueListing, Set<ChunkHash>> initialResult = runInitialBackupOfPairs1And2();
-
-        // Update key 2 and add the new key 3.
-        ShadowBackupDataInput.reset();
-        ShadowBackupDataInput.addEntity(TEST_KEY_2, TEST_VALUE_2B);
-        ShadowBackupDataInput.addEntity(TEST_KEY_3, TEST_VALUE_3);
-
-        KvBackupEncrypter encrypter = createEncrypter(initialResult.first);
-        BackupEncrypter.Result secondResult =
-                encrypter.backup(
-                        mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialResult.second);
-
-        assertThat(secondResult.getAllChunks()).hasSize(3);
-        assertThat(secondResult.getNewChunks()).hasSize(2);
-        EncryptedChunk newChunk2 = secondResult.getNewChunks().get(0);
-        EncryptedChunk newChunk3 = secondResult.getNewChunks().get(1);
-        assertThat(newChunk2.key()).isEqualTo(getChunkHash(TEST_KEY_2, TEST_VALUE_2B));
-        assertThat(decryptChunk(newChunk2).value).isEqualTo(TEST_VALUE_2B);
-        assertThat(newChunk3.key()).isEqualTo(getChunkHash(TEST_KEY_3, TEST_VALUE_3));
-        assertThat(decryptChunk(newChunk3).value).isEqualTo(TEST_VALUE_3);
-    }
-
-    @Test
-    public void backup_allChunksContainsHashesOfAllChunks() throws Exception {
-        Pair<KeyValueListing, Set<ChunkHash>> initialResult = runInitialBackupOfPairs1And2();
-
-        ShadowBackupDataInput.reset();
-        ShadowBackupDataInput.addEntity(TEST_KEY_3, TEST_VALUE_3);
-
-        KvBackupEncrypter encrypter = createEncrypter(initialResult.first);
-        BackupEncrypter.Result secondResult =
-                encrypter.backup(
-                        mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialResult.second);
-
-        assertThat(secondResult.getAllChunks())
-                .containsExactly(
-                        getChunkHash(TEST_KEY_1, TEST_VALUE_1),
-                        getChunkHash(TEST_KEY_2, TEST_VALUE_2),
-                        getChunkHash(TEST_KEY_3, TEST_VALUE_3));
-    }
-
-    @Test
-    public void backup_negativeSize_deletesKeyFromExistingBackup() throws Exception {
-        Pair<KeyValueListing, Set<ChunkHash>> initialResult = runInitialBackupOfPairs1And2();
-
-        ShadowBackupDataInput.reset();
-        ShadowBackupDataInput.addEntity(new DataEntity(TEST_KEY_2));
-
-        KvBackupEncrypter encrypter = createEncrypter(initialResult.first);
-        Result secondResult =
-                encrypter.backup(
-                        mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialResult.second);
-
-        assertThat(secondResult.getAllChunks())
-                .containsExactly(getChunkHash(TEST_KEY_1, TEST_VALUE_1));
-        assertThat(secondResult.getNewChunks()).isEmpty();
-    }
-
-    @Test
-    public void backup_returnsMessageDigestOverChunkHashes() throws Exception {
-        Pair<KeyValueListing, Set<ChunkHash>> initialResult = runInitialBackupOfPairs1And2();
-
-        ShadowBackupDataInput.reset();
-        ShadowBackupDataInput.addEntity(TEST_KEY_3, TEST_VALUE_3);
-
-        KvBackupEncrypter encrypter = createEncrypter(initialResult.first);
-        Result secondResult =
-                encrypter.backup(
-                        mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialResult.second);
-
-        MessageDigest messageDigest =
-                MessageDigest.getInstance(BackupEncrypter.MESSAGE_DIGEST_ALGORITHM);
-        ImmutableList<ChunkHash> sortedHashes =
-                Ordering.natural()
-                        .immutableSortedCopy(
-                                ImmutableList.of(
-                                        getChunkHash(TEST_KEY_1, TEST_VALUE_1),
-                                        getChunkHash(TEST_KEY_2, TEST_VALUE_2),
-                                        getChunkHash(TEST_KEY_3, TEST_VALUE_3)));
-        messageDigest.update(sortedHashes.get(0).getHash());
-        messageDigest.update(sortedHashes.get(1).getHash());
-        messageDigest.update(sortedHashes.get(2).getHash());
-        assertThat(secondResult.getDigest()).isEqualTo(messageDigest.digest());
-    }
-
-    @Test
-    public void getNewKeyValueListing_noExistingBackup_returnsCorrectListing() throws Exception {
-        KeyValueListing keyValueListing = runInitialBackupOfPairs1And2().first;
-
-        assertThat(keyValueListing.entries.length).isEqualTo(2);
-        assertThat(keyValueListing.entries[0].key).isEqualTo(TEST_KEY_1);
-        assertThat(keyValueListing.entries[0].hash)
-                .isEqualTo(getChunkHash(TEST_KEY_1, TEST_VALUE_1).getHash());
-        assertThat(keyValueListing.entries[1].key).isEqualTo(TEST_KEY_2);
-        assertThat(keyValueListing.entries[1].hash)
-                .isEqualTo(getChunkHash(TEST_KEY_2, TEST_VALUE_2).getHash());
-    }
-
-    @Test
-    public void getNewKeyValueListing_existingBackup_returnsCorrectListing() throws Exception {
-        Pair<KeyValueListing, Set<ChunkHash>> initialResult = runInitialBackupOfPairs1And2();
-
-        ShadowBackupDataInput.reset();
-        ShadowBackupDataInput.addEntity(TEST_KEY_2, TEST_VALUE_2B);
-        ShadowBackupDataInput.addEntity(TEST_KEY_3, TEST_VALUE_3);
-
-        KvBackupEncrypter encrypter = createEncrypter(initialResult.first);
-        encrypter.backup(mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialResult.second);
-
-        ImmutableMap<String, ChunkHash> keyValueListing =
-                listingToMap(encrypter.getNewKeyValueListing());
-        assertThat(keyValueListing).hasSize(3);
-        assertThat(keyValueListing)
-                .containsEntry(TEST_KEY_1, getChunkHash(TEST_KEY_1, TEST_VALUE_1));
-        assertThat(keyValueListing)
-                .containsEntry(TEST_KEY_2, getChunkHash(TEST_KEY_2, TEST_VALUE_2B));
-        assertThat(keyValueListing)
-                .containsEntry(TEST_KEY_3, getChunkHash(TEST_KEY_3, TEST_VALUE_3));
-    }
-
-    @Test
-    public void getNewKeyValueChunkListing_beforeBackup_throws() throws Exception {
-        KvBackupEncrypter encrypter = createEncrypter(new KeyValueListing());
-        assertThrows(IllegalStateException.class, encrypter::getNewKeyValueListing);
-    }
-
-    private ImmutableMap<String, ChunkHash> listingToMap(KeyValueListing listing) {
-        // We can't use the ImmutableMap collector directly because it isn't supported in Android
-        // guava.
-        return ImmutableMap.copyOf(
-                Arrays.stream(listing.entries)
-                        .collect(
-                                Collectors.toMap(
-                                        entry -> entry.key, entry -> new ChunkHash(entry.hash))));
-    }
-
-    private Pair<KeyValueListing, Set<ChunkHash>> runInitialBackupOfPairs1And2() throws Exception {
-        ShadowBackupDataInput.addEntity(TEST_KEY_1, TEST_VALUE_1);
-        ShadowBackupDataInput.addEntity(TEST_KEY_2, TEST_VALUE_2);
-
-        KeyValueListing initialKeyValueListing = new KeyValueListingBuilder().build();
-        ImmutableSet<ChunkHash> initialExistingChunks = ImmutableSet.of();
-        KvBackupEncrypter encrypter = createEncrypter(initialKeyValueListing);
-        Result firstResult =
-                encrypter.backup(
-                        mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialExistingChunks);
-
-        return Pair.create(
-                encrypter.getNewKeyValueListing(), ImmutableSet.copyOf(firstResult.getAllChunks()));
-    }
-
-    private ChunkHash getChunkHash(String key, byte[] value) throws Exception {
-        KeyValuePair pair = new KeyValuePair();
-        pair.key = key;
-        pair.value = Arrays.copyOf(value, value.length);
-        return mChunkHasher.computeHash(KeyValuePair.toByteArray(pair));
-    }
-
-    private KeyValuePair decryptChunk(EncryptedChunk encryptedChunk) throws Exception {
-        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
-        cipher.init(
-                Cipher.DECRYPT_MODE,
-                mSecretKey,
-                new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * Byte.SIZE, encryptedChunk.nonce()));
-        byte[] decryptedBytes = cipher.doFinal(encryptedChunk.encryptedBytes());
-        return KeyValuePair.parseFrom(decryptedBytes);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTaskTest.java
deleted file mode 100644
index cda7317..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTaskTest.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertFalse;
-
-import android.app.Application;
-import android.platform.test.annotations.Presubmit;
-import android.security.keystore.recovery.RecoveryController;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.keys.KeyWrapUtils;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
-import com.android.server.backup.encryption.keys.TertiaryKeyStore;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-import com.android.server.testing.fakes.FakeCryptoBackupServer;
-import com.android.server.testing.shadows.ShadowRecoveryController;
-
-import java.security.SecureRandom;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.crypto.SecretKey;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-@Config(shadows = {ShadowRecoveryController.class, ShadowRecoveryController.class})
-public class RotateSecondaryKeyTaskTest {
-    private static final String APP_1 = "app1";
-    private static final String APP_2 = "app2";
-    private static final String APP_3 = "app3";
-
-    private static final String CURRENT_SECONDARY_KEY_ALIAS =
-            "recoverablekey.alias/d524796bd07de3c2225c63d434eff698";
-    private static final String NEXT_SECONDARY_KEY_ALIAS =
-            "recoverablekey.alias/6c6d198a7f12e662b6bc45f4849db170";
-
-    private Application mApplication;
-    private RotateSecondaryKeyTask mTask;
-    private RecoveryController mRecoveryController;
-    private FakeCryptoBackupServer mBackupServer;
-    private CryptoSettings mCryptoSettings;
-    private Map<String, SecretKey> mTertiaryKeysByPackageName;
-    private RecoverableKeyStoreSecondaryKeyManager mRecoverableSecondaryKeyManager;
-
-    @Before
-    public void setUp() throws Exception {
-        mApplication = ApplicationProvider.getApplicationContext();
-
-        mTertiaryKeysByPackageName = new HashMap<>();
-        mTertiaryKeysByPackageName.put(APP_1, generateAesKey());
-        mTertiaryKeysByPackageName.put(APP_2, generateAesKey());
-        mTertiaryKeysByPackageName.put(APP_3, generateAesKey());
-
-        mRecoveryController = RecoveryController.getInstance(mApplication);
-        mRecoverableSecondaryKeyManager =
-                new RecoverableKeyStoreSecondaryKeyManager(
-                        RecoveryController.getInstance(mApplication), new SecureRandom());
-        mBackupServer = new FakeCryptoBackupServer();
-        mCryptoSettings = CryptoSettings.getInstanceForTesting(mApplication);
-        addNextSecondaryKeyToRecoveryController();
-        mCryptoSettings.setNextSecondaryAlias(NEXT_SECONDARY_KEY_ALIAS);
-
-        mTask =
-                new RotateSecondaryKeyTask(
-                        mApplication,
-                        mRecoverableSecondaryKeyManager,
-                        mBackupServer,
-                        mCryptoSettings,
-                        mRecoveryController);
-
-        ShadowRecoveryController.reset();
-    }
-
-    @Test
-    public void run_failsIfThereIsNoActiveSecondaryKey() throws Exception {
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-        addCurrentSecondaryKeyToRecoveryController();
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-
-        mTask.run();
-
-        assertFalse(mCryptoSettings.getActiveSecondaryKeyAlias().isPresent());
-    }
-
-    @Test
-    public void run_failsIfActiveSecondaryIsNotInRecoveryController() throws Exception {
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-        // Have to add it first as otherwise CryptoSettings throws an exception when trying to set
-        // it
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-
-        mTask.run();
-
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
-                .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
-    }
-
-    @Test
-    public void run_doesNothingIfFlagIsDisabled() throws Exception {
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-        addWrappedTertiaries();
-
-        mTask.run();
-
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
-                .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
-    }
-
-    @Test
-    public void run_setsActiveSecondary() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-        addWrappedTertiaries();
-
-        mTask.run();
-
-        assertThat(mBackupServer.getActiveSecondaryKeyAlias().get())
-                .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
-    }
-
-    @Test
-    public void run_rewrapsExistingTertiaryKeys() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-        addWrappedTertiaries();
-
-        mTask.run();
-
-        Map<String, WrappedKeyProto.WrappedKey> rewrappedKeys =
-                mBackupServer.getAllTertiaryKeys(NEXT_SECONDARY_KEY_ALIAS);
-        SecretKey secondaryKey = (SecretKey) mRecoveryController.getKey(NEXT_SECONDARY_KEY_ALIAS);
-        for (String packageName : mTertiaryKeysByPackageName.keySet()) {
-            WrappedKeyProto.WrappedKey rewrappedKey = rewrappedKeys.get(packageName);
-            assertThat(KeyWrapUtils.unwrap(secondaryKey, rewrappedKey))
-                    .isEqualTo(mTertiaryKeysByPackageName.get(packageName));
-        }
-    }
-
-    @Test
-    public void run_persistsRewrappedKeysToDisk() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-        addWrappedTertiaries();
-
-        mTask.run();
-
-        RecoverableKeyStoreSecondaryKey secondaryKey = getRecoverableKey(NEXT_SECONDARY_KEY_ALIAS);
-        Map<String, SecretKey> keys =
-                TertiaryKeyStore.newInstance(mApplication, secondaryKey).getAll();
-        for (String packageName : mTertiaryKeysByPackageName.keySet()) {
-            SecretKey tertiaryKey = mTertiaryKeysByPackageName.get(packageName);
-            SecretKey newlyWrappedKey = keys.get(packageName);
-            assertThat(tertiaryKey.getEncoded()).isEqualTo(newlyWrappedKey.getEncoded());
-        }
-    }
-
-    @Test
-    public void run_stillSetsActiveSecondaryIfNoTertiaries() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-
-        mTask.run();
-
-        assertThat(mBackupServer.getActiveSecondaryKeyAlias().get())
-                .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
-    }
-
-    @Test
-    public void run_setsActiveSecondaryKeyAliasInSettings() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-
-        mTask.run();
-
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
-                .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
-    }
-
-    @Test
-    public void run_removesNextSecondaryKeyAliasInSettings() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-
-        mTask.run();
-
-        assertFalse(mCryptoSettings.getNextSecondaryKeyAlias().isPresent());
-    }
-
-    @Test
-    public void run_deletesOldKeyFromRecoverableKeyStoreLoader() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-
-        mTask.run();
-
-        assertThat(mRecoveryController.getKey(CURRENT_SECONDARY_KEY_ALIAS)).isNull();
-    }
-
-    @Test
-    public void run_doesNotRotateIfNoNextAlias() throws Exception {
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-        mCryptoSettings.removeNextSecondaryKeyAlias();
-
-        mTask.run();
-
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
-                .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
-        assertFalse(mCryptoSettings.getNextSecondaryKeyAlias().isPresent());
-    }
-
-    @Test
-    public void run_doesNotRotateIfKeyIsNotSyncedYet() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-
-        mTask.run();
-
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
-                .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
-    }
-
-    @Test
-    public void run_doesNotClearNextKeyIfSyncIsJustPending() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-
-        mTask.run();
-
-        assertThat(mCryptoSettings.getNextSecondaryKeyAlias().get())
-                .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
-    }
-
-    @Test
-    public void run_doesNotRotateIfPermanentFailure() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-
-        mTask.run();
-
-        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
-                .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
-    }
-
-    @Test
-    public void run_removesNextKeyIfPermanentFailure() throws Exception {
-        addNextSecondaryKeyToRecoveryController();
-        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
-        addCurrentSecondaryKeyToRecoveryController();
-        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
-        mBackupServer.setActiveSecondaryKeyAlias(
-                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
-
-        mTask.run();
-
-        assertFalse(mCryptoSettings.getNextSecondaryKeyAlias().isPresent());
-    }
-
-    private void setNextKeyRecoveryStatus(int status) throws Exception {
-        mRecoveryController.setRecoveryStatus(NEXT_SECONDARY_KEY_ALIAS, status);
-    }
-
-    private void addCurrentSecondaryKeyToRecoveryController() throws Exception {
-        mRecoveryController.generateKey(CURRENT_SECONDARY_KEY_ALIAS);
-    }
-
-    private void addNextSecondaryKeyToRecoveryController() throws Exception {
-        mRecoveryController.generateKey(NEXT_SECONDARY_KEY_ALIAS);
-    }
-
-    private void addWrappedTertiaries() throws Exception {
-        TertiaryKeyStore tertiaryKeyStore =
-                TertiaryKeyStore.newInstance(
-                        mApplication, getRecoverableKey(CURRENT_SECONDARY_KEY_ALIAS));
-
-        for (String packageName : mTertiaryKeysByPackageName.keySet()) {
-            tertiaryKeyStore.save(packageName, mTertiaryKeysByPackageName.get(packageName));
-        }
-    }
-
-    private RecoverableKeyStoreSecondaryKey getRecoverableKey(String alias) throws Exception {
-        return mRecoverableSecondaryKeyManager.get(alias).get();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java
deleted file mode 100644
index 4ac4fa8..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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.encryption.tasks;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-import android.security.keystore.recovery.RecoveryController;
-
-import com.android.server.backup.encryption.CryptoSettings;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
-import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
-import com.android.server.testing.shadows.ShadowRecoveryController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-import java.security.SecureRandom;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowRecoveryController.class})
-@Presubmit
-public class StartSecondaryKeyRotationTaskTest {
-
-    private CryptoSettings mCryptoSettings;
-    private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
-    private StartSecondaryKeyRotationTask mStartSecondaryKeyRotationTask;
-
-    @Before
-    public void setUp() throws Exception {
-        mSecondaryKeyManager =
-                new RecoverableKeyStoreSecondaryKeyManager(
-                        RecoveryController.getInstance(RuntimeEnvironment.application),
-                        new SecureRandom());
-        mCryptoSettings = CryptoSettings.getInstanceForTesting(RuntimeEnvironment.application);
-        mStartSecondaryKeyRotationTask =
-                new StartSecondaryKeyRotationTask(mCryptoSettings, mSecondaryKeyManager);
-
-        ShadowRecoveryController.reset();
-    }
-
-    @Test
-    public void run_doesNothingIfNoActiveSecondaryExists() {
-        mStartSecondaryKeyRotationTask.run();
-
-        assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isFalse();
-    }
-
-    @Test
-    public void run_doesNotRemoveExistingNextSecondaryKeyIfItIsAlreadyActive() throws Exception {
-        generateAnActiveKey();
-        String activeAlias = mCryptoSettings.getActiveSecondaryKeyAlias().get();
-        mCryptoSettings.setNextSecondaryAlias(activeAlias);
-
-        mStartSecondaryKeyRotationTask.run();
-
-        assertThat(mSecondaryKeyManager.get(activeAlias).isPresent()).isTrue();
-    }
-
-    @Test
-    public void run_doesRemoveExistingNextSecondaryKeyIfItIsNotYetActive() throws Exception {
-        generateAnActiveKey();
-        RecoverableKeyStoreSecondaryKey nextKey = mSecondaryKeyManager.generate();
-        String nextAlias = nextKey.getAlias();
-        mCryptoSettings.setNextSecondaryAlias(nextAlias);
-
-        mStartSecondaryKeyRotationTask.run();
-
-        assertThat(mSecondaryKeyManager.get(nextAlias).isPresent()).isFalse();
-    }
-
-    @Test
-    public void run_generatesANewNextSecondaryKey() throws Exception {
-        generateAnActiveKey();
-
-        mStartSecondaryKeyRotationTask.run();
-
-        assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isTrue();
-    }
-
-    @Test
-    public void run_generatesANewKeyThatExistsInKeyStore() throws Exception {
-        generateAnActiveKey();
-
-        mStartSecondaryKeyRotationTask.run();
-
-        String nextAlias = mCryptoSettings.getNextSecondaryKeyAlias().get();
-        assertThat(mSecondaryKeyManager.get(nextAlias).isPresent()).isTrue();
-    }
-
-    private void generateAnActiveKey() throws Exception {
-        RecoverableKeyStoreSecondaryKey secondaryKey = mSecondaryKeyManager.generate();
-        mCryptoSettings.setActiveSecondaryKeyAlias(secondaryKey.getAlias());
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java
deleted file mode 100644
index 7e97924..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * 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.encryption.testing;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Scanner;
-import java.util.regex.Pattern;
-
-/**
- * To be used as part of a fake backup server. Processes a Scotty diff script.
- *
- * <p>A Scotty diff script consists of an ASCII line denoting a command, optionally followed by a
- * range of bytes. Command format is either
- *
- * <ul>
- *   <li>A single 64-bit integer, followed by a new line: this denotes that the given number of
- *       bytes are to follow in the stream. These bytes should be written directly to the new file.
- *   <li>Two 64-bit integers, separated by a hyphen, followed by a new line: this says that the
- *       given range of bytes from the original file ought to be copied into the new file.
- * </ul>
- */
-public class DiffScriptProcessor {
-
-    private static final int COPY_BUFFER_SIZE = 1024;
-
-    private static final String READ_MODE = "r";
-    private static final Pattern VALID_COMMAND_PATTERN = Pattern.compile("^\\d+(-\\d+)?$");
-
-    private final File mInput;
-    private final File mOutput;
-    private final long mInputLength;
-
-    /**
-     * A new instance, with {@code input} as previous file, and {@code output} as new file.
-     *
-     * @param input Previous file from which ranges of bytes are to be copied. This file should be
-     *     immutable.
-     * @param output Output file, to which the new data should be written.
-     * @throws IllegalArgumentException if input does not exist.
-     */
-    public DiffScriptProcessor(File input, File output) {
-        checkArgument(input.exists(), "input file did not exist.");
-        mInput = input;
-        mInputLength = input.length();
-        mOutput = Objects.requireNonNull(output);
-    }
-
-    public void process(InputStream diffScript) throws IOException, MalformedDiffScriptException {
-        RandomAccessFile randomAccessInput = new RandomAccessFile(mInput, READ_MODE);
-
-        try (FileOutputStream outputStream = new FileOutputStream(mOutput)) {
-            while (true) {
-                Optional<String> commandString = readCommand(diffScript);
-                if (!commandString.isPresent()) {
-                    return;
-                }
-                Command command = Command.parse(commandString.get());
-
-                if (command.mIsRange) {
-                    checkFileRange(command.mCount, command.mLimit);
-                    copyRange(randomAccessInput, outputStream, command.mCount, command.mLimit);
-                } else {
-                    long bytesCopied = copyBytes(diffScript, outputStream, command.mCount);
-                    if (bytesCopied < command.mCount) {
-                        throw new MalformedDiffScriptException(
-                                String.format(
-                                        Locale.US,
-                                        "Command to copy %d bytes from diff script, but only %d"
-                                            + " bytes available",
-                                        command.mCount,
-                                        bytesCopied));
-                    }
-                    if (diffScript.read() != '\n') {
-                        throw new MalformedDiffScriptException("Expected new line after bytes.");
-                    }
-                }
-            }
-        }
-    }
-
-    private void checkFileRange(long start, long end) throws MalformedDiffScriptException {
-        if (end < start) {
-            throw new MalformedDiffScriptException(
-                    String.format(
-                            Locale.US,
-                            "Command to copy %d-%d bytes from original file, but %2$d < %1$d.",
-                            start,
-                            end));
-        }
-
-        if (end >= mInputLength) {
-            throw new MalformedDiffScriptException(
-                    String.format(
-                            Locale.US,
-                            "Command to copy %d-%d bytes from original file, but file is only %d"
-                                + " bytes long.",
-                            start,
-                            end,
-                            mInputLength));
-        }
-    }
-
-    /**
-     * Reads a command from the input stream.
-     *
-     * @param inputStream The input.
-     * @return Optional of command, or empty if EOF.
-     */
-    private static Optional<String> readCommand(InputStream inputStream) throws IOException {
-        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-
-        int b;
-        while (!isEndOfCommand(b = inputStream.read())) {
-            byteArrayOutputStream.write(b);
-        }
-
-        byte[] bytes = byteArrayOutputStream.toByteArray();
-        if (bytes.length == 0) {
-            return Optional.empty();
-        } else {
-            return Optional.of(new String(bytes, UTF_8));
-        }
-    }
-
-    /**
-     * If the given output from {@link InputStream#read()} is the end of a command - i.e., a new
-     * line or the EOF.
-     *
-     * @param b The byte or -1.
-     * @return {@code true} if ends the command.
-     */
-    private static boolean isEndOfCommand(int b) {
-        return b == -1 || b == '\n';
-    }
-
-    /**
-     * Copies {@code n} bytes from {@code inputStream} to {@code outputStream}.
-     *
-     * @return The number of bytes copied.
-     * @throws IOException if there was a problem reading or writing.
-     */
-    private static long copyBytes(InputStream inputStream, OutputStream outputStream, long n)
-            throws IOException {
-        byte[] buffer = new byte[COPY_BUFFER_SIZE];
-        long copied = 0;
-        while (n - copied > COPY_BUFFER_SIZE) {
-            long read = copyBlock(inputStream, outputStream, buffer, COPY_BUFFER_SIZE);
-            if (read <= 0) {
-                return copied;
-            }
-        }
-        while (n - copied > 0) {
-            copied += copyBlock(inputStream, outputStream, buffer, (int) (n - copied));
-        }
-        return copied;
-    }
-
-    private static long copyBlock(
-            InputStream inputStream, OutputStream outputStream, byte[] buffer, int size)
-            throws IOException {
-        int read = inputStream.read(buffer, 0, size);
-        outputStream.write(buffer, 0, read);
-        return read;
-    }
-
-    /**
-     * Copies the given range of bytes from the input file to the output stream.
-     *
-     * @param input The input file.
-     * @param output The output stream.
-     * @param start Start position in the input file.
-     * @param end End position in the output file (inclusive).
-     * @throws IOException if there was a problem reading or writing.
-     */
-    private static void copyRange(RandomAccessFile input, OutputStream output, long start, long end)
-            throws IOException {
-        input.seek(start);
-
-        // Inefficient but obviously correct. If tests become slow, optimize.
-        for (; start <= end; start++) {
-            output.write(input.read());
-        }
-    }
-
-    /** Error thrown for a malformed diff script. */
-    public static class MalformedDiffScriptException extends Exception {
-        public MalformedDiffScriptException(String message) {
-            super(message);
-        }
-    }
-
-    /**
-     * A command telling the processor either to insert n bytes, which follow, or copy n-m bytes
-     * from the original file.
-     */
-    private static class Command {
-        private final long mCount;
-        private final long mLimit;
-        private final boolean mIsRange;
-
-        private Command(long count, long limit, boolean isRange) {
-            mCount = count;
-            mLimit = limit;
-            mIsRange = isRange;
-        }
-
-        /**
-         * Attempts to parse the command string into a usable structure.
-         *
-         * @param command The command string, without a new line at the end.
-         * @throws MalformedDiffScriptException if the command is not a valid diff script command.
-         * @return The parsed command.
-         */
-        private static Command parse(String command) throws MalformedDiffScriptException {
-            if (!VALID_COMMAND_PATTERN.matcher(command).matches()) {
-                throw new MalformedDiffScriptException("Bad command: " + command);
-            }
-
-            Scanner commandScanner = new Scanner(command);
-            commandScanner.useDelimiter("-");
-            long n = commandScanner.nextLong();
-            if (!commandScanner.hasNextLong()) {
-                return new Command(n, 0L, /*isRange=*/ false);
-            }
-            long m = commandScanner.nextLong();
-            return new Command(n, m, /*isRange=*/ true);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java
deleted file mode 100644
index 9d2272e..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.encryption.testing;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.AbstractExecutorService;
-import java.util.concurrent.TimeUnit;
-
-/**
- * ExecutorService which needs to be stepped through the jobs in its' queue.
- *
- * <p>This is a deliberately simple implementation because it's only used in testing. The queued
- * jobs are run on the main thread to eliminate any race condition bugs.
- */
-public class QueuingNonAutomaticExecutorService extends AbstractExecutorService {
-
-    private List<Runnable> mWaitingJobs = new ArrayList<>();
-    private int mWaitingJobCount = 0;
-
-    @Override
-    public void shutdown() {
-        mWaitingJobCount = mWaitingJobs.size();
-        mWaitingJobs = null; // This will force an error if jobs are submitted after shutdown
-    }
-
-    @Override
-    public List<Runnable> shutdownNow() {
-        List<Runnable> queuedJobs = mWaitingJobs;
-        shutdown();
-        return queuedJobs;
-    }
-
-    @Override
-    public boolean isShutdown() {
-        return mWaitingJobs == null;
-    }
-
-    @Override
-    public boolean isTerminated() {
-        return mWaitingJobs == null && mWaitingJobCount == 0;
-    }
-
-    @Override
-    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
-        long expiry = System.currentTimeMillis() + unit.toMillis(timeout);
-        for (Runnable job : mWaitingJobs) {
-            if (System.currentTimeMillis() > expiry) {
-                return false;
-            }
-
-            job.run();
-        }
-        return true;
-    }
-
-    @Override
-    public void execute(Runnable command) {
-        mWaitingJobs.add(command);
-    }
-
-    public void runNext() {
-        if (mWaitingJobs.isEmpty()) {
-            throw new IllegalStateException("Attempted to run jobs on an empty paused executor");
-        }
-
-        mWaitingJobs.remove(0).run();
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/testing/RandomInputStream.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/testing/RandomInputStream.java
deleted file mode 100644
index 998da0b..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/testing/RandomInputStream.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.testing;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Random;
-
-/** {@link InputStream} that generates random bytes up to a given length. For testing purposes. */
-public class RandomInputStream extends InputStream {
-    private static final int BYTE_MAX_VALUE = 255;
-
-    private final Random mRandom;
-    private final int mSizeBytes;
-    private int mBytesRead;
-
-    /**
-     * A new instance, generating {@code sizeBytes} from {@code random} as a source.
-     *
-     * @param random Source of random bytes.
-     * @param sizeBytes The number of bytes to generate before closing the stream.
-     */
-    public RandomInputStream(Random random, int sizeBytes) {
-        mRandom = random;
-        mSizeBytes = sizeBytes;
-        mBytesRead = 0;
-    }
-
-    @Override
-    public int read() throws IOException {
-        if (isFinished()) {
-            return -1;
-        }
-        mBytesRead++;
-        return mRandom.nextInt(BYTE_MAX_VALUE);
-    }
-
-    @Override
-    public int read(byte[] b, int off, int len) throws IOException {
-        checkArgument(off + len <= b.length);
-        if (isFinished()) {
-            return -1;
-        }
-        int length = Math.min(len, mSizeBytes - mBytesRead);
-        int end = off + length;
-
-        for (int i = off; i < end; ) {
-            for (int rnd = mRandom.nextInt(), n = Math.min(end - i, Integer.SIZE / Byte.SIZE);
-                    n-- > 0;
-                    rnd >>= Byte.SIZE) {
-                b[i++] = (byte) rnd;
-            }
-        }
-
-        mBytesRead += length;
-        return length;
-    }
-
-    private boolean isFinished() {
-        return mBytesRead >= mSizeBytes;
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java
deleted file mode 100644
index b0c02ba..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2018 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.testing;
-
-import android.util.Pair;
-
-import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
-import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
-
-import java.nio.charset.Charset;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Random;
-
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-
-/** Helpers for crypto code tests. */
-public class CryptoTestUtils {
-    private static final String KEY_ALGORITHM = "AES";
-    private static final int KEY_SIZE_BITS = 256;
-
-    private CryptoTestUtils() {}
-
-    public static SecretKey generateAesKey() throws NoSuchAlgorithmException {
-        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
-        keyGenerator.init(KEY_SIZE_BITS);
-        return keyGenerator.generateKey();
-    }
-
-    /** Generates a byte array of size {@code n} containing random bytes. */
-    public static byte[] generateRandomBytes(int n) {
-        byte[] bytes = new byte[n];
-        Random random = new Random();
-        random.nextBytes(bytes);
-        return bytes;
-    }
-
-    public static ChunksMetadataProto.Chunk newChunk(ChunkHash hash, int length) {
-        return newChunk(hash.getHash(), length);
-    }
-
-    public static ChunksMetadataProto.Chunk newChunk(byte[] hash, int length) {
-        ChunksMetadataProto.Chunk newChunk = new ChunksMetadataProto.Chunk();
-        newChunk.hash = Arrays.copyOf(hash, hash.length);
-        newChunk.length = length;
-        return newChunk;
-    }
-
-    public static ChunksMetadataProto.ChunkListing newChunkListing(
-            String docId,
-            byte[] fingerprintSalt,
-            int cipherType,
-            int orderingType,
-            ChunksMetadataProto.Chunk... chunks) {
-        ChunksMetadataProto.ChunkListing chunkListing =
-                newChunkListingWithoutDocId(fingerprintSalt, cipherType, orderingType, chunks);
-        chunkListing.documentId = docId;
-        return chunkListing;
-    }
-
-    public static ChunksMetadataProto.ChunkListing newChunkListingWithoutDocId(
-            byte[] fingerprintSalt,
-            int cipherType,
-            int orderingType,
-            ChunksMetadataProto.Chunk... chunks) {
-        ChunksMetadataProto.ChunkListing chunkListing = new ChunksMetadataProto.ChunkListing();
-        chunkListing.fingerprintMixerSalt =
-                fingerprintSalt == null
-                        ? null
-                        : Arrays.copyOf(fingerprintSalt, fingerprintSalt.length);
-        chunkListing.cipherType = cipherType;
-        chunkListing.chunkOrderingType = orderingType;
-        chunkListing.chunks = chunks;
-        return chunkListing;
-    }
-
-    public static ChunksMetadataProto.ChunkOrdering newChunkOrdering(
-            int[] starts, byte[] checksum) {
-        ChunksMetadataProto.ChunkOrdering chunkOrdering = new ChunksMetadataProto.ChunkOrdering();
-        chunkOrdering.starts = starts == null ? null : Arrays.copyOf(starts, starts.length);
-        chunkOrdering.checksum =
-                checksum == null ? checksum : Arrays.copyOf(checksum, checksum.length);
-        return chunkOrdering;
-    }
-
-    public static ChunksMetadataProto.ChunksMetadata newChunksMetadata(
-            int cipherType, int checksumType, int chunkOrderingType, byte[] chunkOrdering) {
-        ChunksMetadataProto.ChunksMetadata metadata = new ChunksMetadataProto.ChunksMetadata();
-        metadata.cipherType = cipherType;
-        metadata.checksumType = checksumType;
-        metadata.chunkOrdering = Arrays.copyOf(chunkOrdering, chunkOrdering.length);
-        metadata.chunkOrderingType = chunkOrderingType;
-        return metadata;
-    }
-
-    public static KeyValuePairProto.KeyValuePair newPair(String key, String value) {
-        return newPair(key, value.getBytes(Charset.forName("UTF-8")));
-    }
-
-    public static KeyValuePairProto.KeyValuePair newPair(String key, byte[] value) {
-        KeyValuePairProto.KeyValuePair newPair = new KeyValuePairProto.KeyValuePair();
-        newPair.key = key;
-        newPair.value = value;
-        return newPair;
-    }
-
-    public static ChunksMetadataProto.ChunkListing clone(
-            ChunksMetadataProto.ChunkListing original) {
-        ChunksMetadataProto.Chunk[] clonedChunks;
-        if (original.chunks == null) {
-            clonedChunks = null;
-        } else {
-            clonedChunks = new ChunksMetadataProto.Chunk[original.chunks.length];
-            for (int i = 0; i < original.chunks.length; i++) {
-                clonedChunks[i] = clone(original.chunks[i]);
-            }
-        }
-
-        return newChunkListing(
-                original.documentId,
-                original.fingerprintMixerSalt,
-                original.cipherType,
-                original.chunkOrderingType,
-                clonedChunks);
-    }
-
-    public static ChunksMetadataProto.Chunk clone(ChunksMetadataProto.Chunk original) {
-        return newChunk(original.hash, original.length);
-    }
-
-    public static ChunksMetadataProto.ChunksMetadata clone(
-            ChunksMetadataProto.ChunksMetadata original) {
-        ChunksMetadataProto.ChunksMetadata cloneMetadata = new ChunksMetadataProto.ChunksMetadata();
-        cloneMetadata.chunkOrderingType = original.chunkOrderingType;
-        cloneMetadata.chunkOrdering =
-                original.chunkOrdering == null
-                        ? null
-                        : Arrays.copyOf(original.chunkOrdering, original.chunkOrdering.length);
-        cloneMetadata.checksumType = original.checksumType;
-        cloneMetadata.cipherType = original.cipherType;
-        return cloneMetadata;
-    }
-
-    public static ChunksMetadataProto.ChunkOrdering clone(
-            ChunksMetadataProto.ChunkOrdering original) {
-        ChunksMetadataProto.ChunkOrdering clone = new ChunksMetadataProto.ChunkOrdering();
-        clone.starts = Arrays.copyOf(original.starts, original.starts.length);
-        clone.checksum = Arrays.copyOf(original.checksum, original.checksum.length);
-        return clone;
-    }
-
-    public static <K, V> Map<K, V> mapOf(Pair<K, V>... pairs) {
-        Map<K, V> map = new HashMap<>();
-        for (Pair<K, V> pair : pairs) {
-            map.put(pair.first, pair.second);
-        }
-        return map;
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/TestFileUtils.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/TestFileUtils.java
deleted file mode 100644
index e5d73ba..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/TestFileUtils.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-import com.google.common.io.ByteStreams;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-
-/** Utility methods for use in tests */
-public class TestFileUtils {
-    /** Read the contents of a file into a byte array */
-    public static byte[] toByteArray(File file) throws IOException {
-        try (FileInputStream fis = new FileInputStream(file)) {
-            return ByteStreams.toByteArray(fis);
-        }
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServer.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServer.java
deleted file mode 100644
index 3329060..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServer.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2018 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.testing.fakes;
-
-import android.annotation.Nullable;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.backup.encryption.client.CryptoBackupServer;
-import com.android.server.backup.encryption.client.UnexpectedActiveSecondaryOnServerException;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Optional;
-
-/** Fake {@link CryptoBackupServer}, for tests. Stores tertiary keys in memory. */
-public class FakeCryptoBackupServer implements CryptoBackupServer {
-    @GuardedBy("this")
-    @Nullable
-    private String mActiveSecondaryKeyAlias;
-
-    // Secondary key alias -> (package name -> tertiary key)
-    @GuardedBy("this")
-    private Map<String, Map<String, WrappedKeyProto.WrappedKey>> mWrappedKeyStore = new HashMap<>();
-
-    @Override
-    public String uploadIncrementalBackup(
-            String packageName,
-            String oldDocId,
-            byte[] diffScript,
-            WrappedKeyProto.WrappedKey tertiaryKey) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String uploadNonIncrementalBackup(
-            String packageName, byte[] data, WrappedKeyProto.WrappedKey tertiaryKey) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public synchronized void setActiveSecondaryKeyAlias(
-            String keyAlias, Map<String, WrappedKeyProto.WrappedKey> tertiaryKeys) {
-        mActiveSecondaryKeyAlias = keyAlias;
-
-        mWrappedKeyStore.putIfAbsent(keyAlias, new HashMap<>());
-        Map<String, WrappedKeyProto.WrappedKey> keyStore = mWrappedKeyStore.get(keyAlias);
-
-        for (String packageName : tertiaryKeys.keySet()) {
-            keyStore.put(packageName, tertiaryKeys.get(packageName));
-        }
-    }
-
-    public synchronized Optional<String> getActiveSecondaryKeyAlias() {
-        return Optional.ofNullable(mActiveSecondaryKeyAlias);
-    }
-
-    public synchronized Map<String, WrappedKeyProto.WrappedKey> getAllTertiaryKeys(
-            String secondaryKeyAlias) throws UnexpectedActiveSecondaryOnServerException {
-        if (!secondaryKeyAlias.equals(mActiveSecondaryKeyAlias)) {
-            throw new UnexpectedActiveSecondaryOnServerException(
-                    String.format(
-                            Locale.US,
-                            "Requested tertiary keys wrapped with %s but %s was active secondary.",
-                            secondaryKeyAlias,
-                            mActiveSecondaryKeyAlias));
-        }
-
-        if (!mWrappedKeyStore.containsKey(secondaryKeyAlias)) {
-            return Collections.emptyMap();
-        }
-        return new HashMap<>(mWrappedKeyStore.get(secondaryKeyAlias));
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServerTest.java
deleted file mode 100644
index 4cd8333b..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServerTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.testing.fakes;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertThrows;
-
-import android.util.Pair;
-
-import com.android.server.backup.encryption.client.UnexpectedActiveSecondaryOnServerException;
-import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.nio.charset.Charset;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-public class FakeCryptoBackupServerTest {
-    private static final String PACKAGE_NAME_1 = "package1";
-    private static final String PACKAGE_NAME_2 = "package2";
-    private static final String PACKAGE_NAME_3 = "package3";
-    private static final WrappedKeyProto.WrappedKey PACKAGE_KEY_1 = createWrappedKey("key1");
-    private static final WrappedKeyProto.WrappedKey PACKAGE_KEY_2 = createWrappedKey("key2");
-    private static final WrappedKeyProto.WrappedKey PACKAGE_KEY_3 = createWrappedKey("key3");
-
-    private FakeCryptoBackupServer mServer;
-
-    @Before
-    public void setUp() {
-        mServer = new FakeCryptoBackupServer();
-    }
-
-    @Test
-    public void getActiveSecondaryKeyAlias_isInitiallyAbsent() throws Exception {
-        assertFalse(mServer.getActiveSecondaryKeyAlias().isPresent());
-    }
-
-    @Test
-    public void setActiveSecondaryKeyAlias_setsTheKeyAlias() throws Exception {
-        String keyAlias = "test";
-        mServer.setActiveSecondaryKeyAlias(keyAlias, Collections.emptyMap());
-        assertThat(mServer.getActiveSecondaryKeyAlias().get()).isEqualTo(keyAlias);
-    }
-
-    @Test
-    public void getAllTertiaryKeys_returnsWrappedKeys() throws Exception {
-        Map<String, WrappedKeyProto.WrappedKey> entries =
-                createKeyMap(
-                        new Pair<>(PACKAGE_NAME_1, PACKAGE_KEY_1),
-                        new Pair<>(PACKAGE_NAME_2, PACKAGE_KEY_2));
-        String secondaryKeyAlias = "doge";
-        mServer.setActiveSecondaryKeyAlias(secondaryKeyAlias, entries);
-
-        assertThat(mServer.getAllTertiaryKeys(secondaryKeyAlias)).containsExactlyEntriesIn(entries);
-    }
-
-    @Test
-    public void addTertiaryKeys_updatesExistingSet() throws Exception {
-        String keyId = "karlin";
-        WrappedKeyProto.WrappedKey replacementKey = createWrappedKey("some replacement bytes");
-
-        mServer.setActiveSecondaryKeyAlias(
-                keyId,
-                createKeyMap(
-                        new Pair<>(PACKAGE_NAME_1, PACKAGE_KEY_1),
-                        new Pair<>(PACKAGE_NAME_2, PACKAGE_KEY_2)));
-
-        mServer.setActiveSecondaryKeyAlias(
-                keyId,
-                createKeyMap(
-                        new Pair<>(PACKAGE_NAME_1, replacementKey),
-                        new Pair<>(PACKAGE_NAME_3, PACKAGE_KEY_3)));
-
-        assertThat(mServer.getAllTertiaryKeys(keyId))
-                .containsExactlyEntriesIn(
-                        createKeyMap(
-                                new Pair<>(PACKAGE_NAME_1, replacementKey),
-                                new Pair<>(PACKAGE_NAME_2, PACKAGE_KEY_2),
-                                new Pair<>(PACKAGE_NAME_3, PACKAGE_KEY_3)));
-    }
-
-    @Test
-    public void getAllTertiaryKeys_throwsForUnknownSecondaryKeyAlias() throws Exception {
-        assertThrows(
-                UnexpectedActiveSecondaryOnServerException.class,
-                () -> mServer.getAllTertiaryKeys("unknown"));
-    }
-
-    @Test
-    public void uploadIncrementalBackup_throwsUnsupportedOperationException() {
-        assertThrows(
-                UnsupportedOperationException.class,
-                () ->
-                        mServer.uploadIncrementalBackup(
-                                PACKAGE_NAME_1,
-                                "docid",
-                                new byte[0],
-                                new WrappedKeyProto.WrappedKey()));
-    }
-
-    @Test
-    public void uploadNonIncrementalBackup_throwsUnsupportedOperationException() {
-        assertThrows(
-                UnsupportedOperationException.class,
-                () ->
-                        mServer.uploadNonIncrementalBackup(
-                                PACKAGE_NAME_1, new byte[0], new WrappedKeyProto.WrappedKey()));
-    }
-
-    private static WrappedKeyProto.WrappedKey createWrappedKey(String data) {
-        WrappedKeyProto.WrappedKey wrappedKey = new WrappedKeyProto.WrappedKey();
-        wrappedKey.key = data.getBytes(Charset.forName("UTF-8"));
-        return wrappedKey;
-    }
-
-    private Map<String, WrappedKeyProto.WrappedKey> createKeyMap(
-            Pair<String, WrappedKeyProto.WrappedKey>... pairs) {
-        Map<String, WrappedKeyProto.WrappedKey> map = new HashMap<>();
-        for (Pair<String, WrappedKeyProto.WrappedKey> pair : pairs) {
-            map.put(pair.first, pair.second);
-        }
-        return map;
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java
deleted file mode 100644
index 06f4859..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.testing.shadows;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * Represents a key value pair in {@link ShadowBackupDataInput} and {@link ShadowBackupDataOutput}.
- */
-public class DataEntity {
-    public final String mKey;
-    public final byte[] mValue;
-    public final int mSize;
-
-    /**
-     * Constructs a pair with a string value. The value will be converted to a byte array in {@link
-     * StandardCharsets#UTF_8}.
-     */
-    public DataEntity(String key, String value) {
-        this.mKey = Objects.requireNonNull(key);
-        this.mValue = value.getBytes(StandardCharsets.UTF_8);
-        mSize = this.mValue.length;
-    }
-
-    /**
-     * Constructs a new entity with the given key but a negative size. This represents a deleted
-     * pair.
-     */
-    public DataEntity(String key) {
-        this.mKey = Objects.requireNonNull(key);
-        mSize = -1;
-        mValue = null;
-    }
-
-    /** Constructs a new entity where the size of the value is the entire array. */
-    public DataEntity(String key, byte[] value) {
-        this(key, value, value.length);
-    }
-
-    /**
-     * Constructs a new entity.
-     *
-     * @param key the key of the pair
-     * @param data the value to associate with the key
-     * @param size the length of the value in bytes
-     */
-    public DataEntity(String key, byte[] data, int size) {
-        this.mKey = Objects.requireNonNull(key);
-        this.mSize = size;
-        mValue = new byte[size];
-        for (int i = 0; i < size; i++) {
-            mValue[i] = data[i];
-        }
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DataEntity that = (DataEntity) o;
-
-        if (mSize != that.mSize) {
-            return false;
-        }
-        if (!mKey.equals(that.mKey)) {
-            return false;
-        }
-        return Arrays.equals(mValue, that.mValue);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = mKey.hashCode();
-        result = 31 * result + Arrays.hashCode(mValue);
-        result = 31 * result + mSize;
-        return result;
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataInput.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
deleted file mode 100644
index 7ac6ec4..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.testing.shadows;
-
-import static com.google.common.base.Preconditions.checkState;
-
-import android.annotation.Nullable;
-import android.app.backup.BackupDataInput;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-import java.io.ByteArrayInputStream;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/** Shadow for BackupDataInput. */
-@Implements(BackupDataInput.class)
-public class ShadowBackupDataInput {
-    private static final List<DataEntity> ENTITIES = new ArrayList<>();
-    @Nullable private static IOException sReadNextHeaderException;
-
-    @Nullable private ByteArrayInputStream mCurrentEntityInputStream;
-    private int mCurrentEntity = -1;
-
-    /** Resets the shadow, clearing any entities or exception. */
-    public static void reset() {
-        ENTITIES.clear();
-        sReadNextHeaderException = null;
-    }
-
-    /** Sets the exception which the input will throw for any call to {@link #readNextHeader}. */
-    public static void setReadNextHeaderException(@Nullable IOException readNextHeaderException) {
-        ShadowBackupDataInput.sReadNextHeaderException = readNextHeaderException;
-    }
-
-    /** Adds the given entity to the input. */
-    public static void addEntity(DataEntity e) {
-        ENTITIES.add(e);
-    }
-
-    /** Adds an entity to the input with the given key and value. */
-    public static void addEntity(String key, byte[] value) {
-        ENTITIES.add(new DataEntity(key, value, value.length));
-    }
-
-    public void __constructor__(FileDescriptor fd) {}
-
-    @Implementation
-    public boolean readNextHeader() throws IOException {
-        if (sReadNextHeaderException != null) {
-            throw sReadNextHeaderException;
-        }
-
-        mCurrentEntity++;
-
-        if (mCurrentEntity >= ENTITIES.size()) {
-            return false;
-        }
-
-        byte[] value = ENTITIES.get(mCurrentEntity).mValue;
-        if (value == null) {
-            mCurrentEntityInputStream = new ByteArrayInputStream(new byte[0]);
-        } else {
-            mCurrentEntityInputStream = new ByteArrayInputStream(value);
-        }
-        return true;
-    }
-
-    @Implementation
-    public String getKey() {
-        return ENTITIES.get(mCurrentEntity).mKey;
-    }
-
-    @Implementation
-    public int getDataSize() {
-        return ENTITIES.get(mCurrentEntity).mSize;
-    }
-
-    @Implementation
-    public void skipEntityData() {
-        // Do nothing.
-    }
-
-    @Implementation
-    public int readEntityData(byte[] data, int offset, int size) {
-        checkState(mCurrentEntityInputStream != null, "Must call readNextHeader() first");
-        return mCurrentEntityInputStream.read(data, offset, size);
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
deleted file mode 100644
index 2302e55..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.testing.shadows;
-
-import android.app.backup.BackupDataOutput;
-
-import java.io.FileDescriptor;
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.junit.Assert;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-/** Shadow for BackupDataOutput. */
-@Implements(BackupDataOutput.class)
-public class ShadowBackupDataOutput {
-    private static final List<DataEntity> ENTRIES = new ArrayList<>();
-
-    private String mCurrentKey;
-    private int mDataSize;
-
-    public static void reset() {
-        ENTRIES.clear();
-    }
-
-    public static Set<DataEntity> getEntities() {
-        return new LinkedHashSet<>(ENTRIES);
-    }
-
-    public void __constructor__(FileDescriptor fd) {}
-
-    public void __constructor__(FileDescriptor fd, long quota) {}
-
-    public void __constructor__(FileDescriptor fd, long quota, int transportFlags) {}
-
-    @Implementation
-    public int writeEntityHeader(String key, int size) {
-        mCurrentKey = key;
-        mDataSize = size;
-        return 0;
-    }
-
-    @Implementation
-    public int writeEntityData(byte[] data, int size) {
-        Assert.assertEquals("ShadowBackupDataOutput expects size = mDataSize", size, mDataSize);
-        ENTRIES.add(new DataEntity(mCurrentKey, data, mDataSize));
-        return 0;
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
deleted file mode 100644
index 9c06d81..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2018 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.testing.shadows;
-
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-/** Shadow {@link InternalRecoveryServiceException}. */
-@Implements(InternalRecoveryServiceException.class)
-public class ShadowInternalRecoveryServiceException {
-    private String mMessage;
-
-    @Implementation
-    public void __constructor__(String message) {
-        mMessage = message;
-    }
-
-    @Implementation
-    public void __constructor__(String message, Throwable cause) {
-        mMessage = message;
-    }
-
-    @Implementation
-    public String getMessage() {
-        return mMessage;
-    }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowRecoveryController.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowRecoveryController.java
deleted file mode 100644
index 7dad8a4..0000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowRecoveryController.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2018 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.testing.shadows;
-
-import android.content.Context;
-import android.security.keystore.recovery.InternalRecoveryServiceException;
-import android.security.keystore.recovery.LockScreenRequiredException;
-import android.security.keystore.recovery.RecoveryController;
-
-import com.google.common.collect.ImmutableList;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
-
-import java.lang.reflect.Constructor;
-import java.security.Key;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
-import java.util.HashMap;
-import java.util.List;
-
-import javax.crypto.KeyGenerator;
-
-/**
- * Shadow of {@link RecoveryController}.
- *
- * <p>Instead of generating keys via the {@link RecoveryController}, this shadow generates them in
- * memory.
- */
-@Implements(RecoveryController.class)
-public class ShadowRecoveryController {
-    private static final String KEY_GENERATOR_ALGORITHM = "AES";
-    private static final int KEY_SIZE_BITS = 256;
-
-    private static boolean sIsSupported = true;
-    private static boolean sThrowsInternalError = false;
-    private static HashMap<String, Key> sKeysByAlias = new HashMap<>();
-    private static HashMap<String, Integer> sKeyStatusesByAlias = new HashMap<>();
-
-    @Implementation
-    public void __constructor__() {
-        // do not throw
-    }
-
-    @Implementation
-    public static RecoveryController getInstance(Context context) {
-        // Call non-public constructor.
-        try {
-            Constructor<RecoveryController> constructor = RecoveryController.class.getConstructor();
-            return constructor.newInstance();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Implementation
-    public static boolean isRecoverableKeyStoreEnabled(Context context) {
-        return sIsSupported;
-    }
-
-    @Implementation
-    public Key generateKey(String alias)
-            throws InternalRecoveryServiceException, LockScreenRequiredException {
-        maybeThrowError();
-        KeyGenerator keyGenerator;
-        try {
-            keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM);
-        } catch (NoSuchAlgorithmException e) {
-            // Should never happen
-            throw new RuntimeException(e);
-        }
-
-        keyGenerator.init(KEY_SIZE_BITS);
-        Key key = keyGenerator.generateKey();
-        sKeysByAlias.put(alias, key);
-        sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
-        return key;
-    }
-
-    @Implementation
-    public Key getKey(String alias)
-            throws InternalRecoveryServiceException, UnrecoverableKeyException {
-        return sKeysByAlias.get(alias);
-    }
-
-    @Implementation
-    public void removeKey(String alias) throws InternalRecoveryServiceException {
-        sKeyStatusesByAlias.remove(alias);
-        sKeysByAlias.remove(alias);
-    }
-
-    @Implementation
-    public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException {
-        maybeThrowError();
-        return sKeyStatusesByAlias.getOrDefault(
-                alias, RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
-    }
-
-    @Implementation
-    public List<String> getAliases() throws InternalRecoveryServiceException {
-        return ImmutableList.copyOf(sKeyStatusesByAlias.keySet());
-    }
-
-    private static void maybeThrowError() throws InternalRecoveryServiceException {
-        if (sThrowsInternalError) {
-            throw new InternalRecoveryServiceException("test error");
-        }
-    }
-
-    /** Sets the recovery status of the key with {@code alias} to {@code status}. */
-    public static void setRecoveryStatus(String alias, int status) {
-        sKeyStatusesByAlias.put(alias, status);
-    }
-
-    /** Sets all existing keys to being synced. */
-    public static void syncAllKeys() {
-        for (String alias : sKeysByAlias.keySet()) {
-            sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNCED);
-        }
-    }
-
-    public static void setThrowsInternalError(boolean throwsInternalError) {
-        ShadowRecoveryController.sThrowsInternalError = throwsInternalError;
-    }
-
-    public static void setIsSupported(boolean isSupported) {
-        ShadowRecoveryController.sIsSupported = isSupported;
-    }
-
-    @Resetter
-    public static void reset() {
-        sIsSupported = true;
-        sThrowsInternalError = false;
-        sKeysByAlias.clear();
-        sKeyStatusesByAlias.clear();
-    }
-}
diff --git a/packages/BackupEncryption/test/unittest/Android.bp b/packages/BackupEncryption/test/unittest/Android.bp
deleted file mode 100644
index f005170..0000000
--- a/packages/BackupEncryption/test/unittest/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
-    name: "BackupEncryptionUnitTests",
-    srcs: ["src/**/*.java"],
-    static_libs: [
-        "androidx.test.runner",
-        "androidx.test.rules",
-        "mockito-target-minus-junit4",
-        "platform-test-annotations",
-        "truth-prebuilt",
-        "testables",
-        "testng",
-    ],
-    libs: [
-        "android.test.mock",
-        "android.test.base",
-        "android.test.runner",
-        "BackupEncryption",
-    ],
-    test_suites: ["device-tests"],
-    instrumentation_for: "BackupEncryption",
-    certificate: "platform",
-}
diff --git a/packages/BackupEncryption/test/unittest/AndroidManifest.xml b/packages/BackupEncryption/test/unittest/AndroidManifest.xml
deleted file mode 100644
index 39ac8aa3..0000000
--- a/packages/BackupEncryption/test/unittest/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.server.backup.encryption.unittests"
-          android:sharedUserId="android.uid.system" >
-    <application android:testOnly="true">
-        <uses-library android:name="android.test.runner" />
-    </application>
-    <instrumentation
-        android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.server.backup.encryption"
-        android:label="Backup Encryption Unit Tests" />
-</manifest>
\ No newline at end of file
diff --git a/packages/BackupEncryption/test/unittest/AndroidTest.xml b/packages/BackupEncryption/test/unittest/AndroidTest.xml
deleted file mode 100644
index c9c812a..0000000
--- a/packages/BackupEncryption/test/unittest/AndroidTest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-<configuration description="Runs Backup Encryption Unit Tests.">
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="install-arg" value="-t" />
-        <option name="test-file-name" value="BackupEncryptionUnitTests.apk" />
-    </target_preparer>
-
-    <option name="test-tag" value="BackupEncryptionUnitTests" />
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="com.android.server.backup.encryption.unittests" />
-        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
-    </test>
-</configuration>
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 2901705..2c24bf1 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -3,6 +3,7 @@
   <string name="string_cancel">Cancel</string>
   <string name="string_continue">Continue</string>
   <string name="string_more_options">More options</string>
+  <string name="string_create_at_another_place">Create at another place</string>
   <string name="string_no_thanks">No thanks</string>
   <string name="passkey_creation_intro_title">A simple way to sign in safely</string>
   <string name="passkey_creation_intro_body">Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more</string>
@@ -10,4 +11,5 @@
   <string name="choose_provider_body">This provider will store passkeys and passwords for you and help you easily autofill and sign in. Learn more</string>
   <string name="choose_create_option_title">Create a passkey at</string>
   <string name="choose_sign_in_title">Use saved sign in</string>
+  <string name="create_passkey_at">Create passkey at</string>
 </resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 5918633..46bf19c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -1,6 +1,14 @@
 package com.android.credentialmanager
 
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
 import android.content.Context
+import android.content.Intent
+import android.credentials.ui.Entry
+import android.credentials.ui.ProviderData
+import android.credentials.ui.RequestInfo
+import android.graphics.drawable.Icon
+import android.os.Binder
 import com.android.credentialmanager.createflow.CreateOptionInfo
 import com.android.credentialmanager.createflow.CreatePasskeyUiState
 import com.android.credentialmanager.createflow.CreateScreenState
@@ -11,8 +19,82 @@
 
 // Consider repo per screen, similar to view model?
 class CredentialManagerRepo(
-  private val context: Context
+  private val context: Context,
+  intent: Intent,
 ) {
+  private val requestInfo: RequestInfo
+  private val providerList: List<ProviderData>
+
+  init {
+    requestInfo = intent.extras?.getParcelable(
+      RequestInfo.EXTRA_REQUEST_INFO,
+      RequestInfo::class.java
+    ) ?: RequestInfo(
+      Binder(),
+      RequestInfo.TYPE_CREATE,
+      /*isFirstUsage=*/false
+    )
+
+    providerList = intent.extras?.getParcelableArrayList(
+      ProviderData.EXTRA_PROVIDER_DATA_LIST,
+      ProviderData::class.java
+    ) ?: testProviderList()
+  }
+
+  private fun testProviderList(): List<ProviderData> {
+    return listOf(
+      ProviderData(
+        "com.google",
+        listOf<Entry>(
+          newEntry(1, "elisa.beckett@gmail.com", "Elisa Backett"),
+          newEntry(2, "elisa.work@google.com", "Elisa Backett Work"),
+        ),
+        listOf<Entry>(
+          newEntry(3, "Go to Settings", ""),
+          newEntry(4, "Switch Account", ""),
+        ),
+        null
+      ),
+      ProviderData(
+        "com.dashlane",
+        listOf<Entry>(
+          newEntry(5, "elisa.beckett@dashlane.com", "Elisa Backett"),
+          newEntry(6, "elisa.work@dashlane.com", "Elisa Backett Work"),
+        ),
+        listOf<Entry>(
+          newEntry(7, "Manage Accounts", "Manage your accounts in the dashlane app"),
+        ),
+        null
+      ),
+      ProviderData(
+        "com.lastpass",
+        listOf<Entry>(
+          newEntry(8, "elisa.beckett@lastpass.com", "Elisa Backett"),
+        ),
+        listOf<Entry>(),
+        null
+      )
+
+    )
+  }
+
+  private fun newEntry(id: Int, title: String, subtitle: String): Entry {
+    val slice = Slice.Builder(
+      Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1)
+    )
+      .addText(title, null, listOf(Entry.HINT_TITLE))
+      .addText(subtitle, null, listOf(Entry.HINT_SUBTITLE))
+      .addIcon(
+        Icon.createWithResource(context, R.drawable.ic_passkey),
+        null,
+        listOf(Entry.HINT_ICON))
+      .build()
+    return Entry(
+      id,
+      slice
+    )
+  }
+
   private fun getCredentialProviderList():
     List<com.android.credentialmanager.getflow.ProviderInfo> {
       return listOf(
@@ -140,8 +222,11 @@
   companion object {
     lateinit var repo: CredentialManagerRepo
 
-    fun setup(context: Context) {
-      repo = CredentialManagerRepo(context)
+    fun setup(
+      context: Context,
+      intent: Intent,
+    ) {
+      repo = CredentialManagerRepo(context, intent)
     }
 
     fun getInstance(): CredentialManagerRepo {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 5cd6a13..98c824c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -15,7 +15,7 @@
 class CredentialSelectorActivity : ComponentActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
-    CredentialManagerRepo.setup(this)
+    CredentialManagerRepo.setup(this, intent)
     val startDestination = intent.extras?.getString(
       "start_destination",
       "CREATE_PASSKEY"
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index 5aa1e9b..044688b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -22,4 +22,5 @@
   PASSKEY_INTRO,
   PROVIDER_SELECTION,
   CREATION_OPTION_SELECTION,
+  MORE_OPTIONS_SELECTION,
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
index fbec1bc..997519d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
@@ -19,9 +19,13 @@
 import androidx.compose.material.Divider
 import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
 import androidx.compose.material.ModalBottomSheetLayout
 import androidx.compose.material.ModalBottomSheetValue
 import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
 import androidx.compose.material.rememberModalBottomSheetState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -71,8 +75,13 @@
           onOptionSelected = {viewModel.onCreateOptionSelected(it)},
           onCancel = cancelActivity,
           multiProvider = uiState.providers.size > 1,
-          onMoreOptionSelected = {viewModel.onMoreOptionSelected()}
+          onMoreOptionSelected = {viewModel.onMoreOptionSelected(it)}
         )
+        CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionSelectionCard(
+            providerInfo = uiState.selectedProvider!!,
+            onCancel = cancelActivity,
+            onBackButtonSelected = {viewModel.onBackButtonSelected(it)}
+          )
       }
     },
     scrimColor = Color.Transparent,
@@ -202,6 +211,67 @@
   }
 }
 
+@Composable
+fun MoreOptionSelectionCard(
+  providerInfo: ProviderInfo,
+  onCancel: () -> Unit,
+  onBackButtonSelected: (String) -> Unit
+) {
+  Card(
+    backgroundColor = lightBackgroundColor,
+  ) {
+    Column() {
+      TopAppBar(
+        title = { Text(text = stringResource(R.string.string_more_options), style = Typography.subtitle1) },
+        backgroundColor = lightBackgroundColor,
+        elevation = 0.dp,
+        navigationIcon =
+        {
+          IconButton(onClick = { onBackButtonSelected(providerInfo.name) }) {
+            Icon(Icons.Filled.ArrowBack, "backIcon"
+            )
+          }
+        }
+      )
+      Divider(
+         thickness = 24.dp,
+         color = Color.Transparent
+      )
+      Text(
+        text = stringResource(R.string.create_passkey_at),
+        style = Typography.body1,
+        modifier = Modifier.padding(horizontal = 28.dp)
+      )
+      Card(
+        shape = Shapes.medium,
+        modifier = Modifier
+          .padding(horizontal = 24.dp)
+          .align(alignment = Alignment.CenterHorizontally)
+      ) {
+        LazyColumn(
+          verticalArrangement = Arrangement.spacedBy(2.dp)
+        ) {
+        }
+      }
+      Divider(
+        thickness = 24.dp,
+        color = Color.Transparent
+      )
+      Row(
+        horizontalArrangement = Arrangement.Start,
+        modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
+      ) {
+        CancelButton(stringResource(R.string.string_cancel), onCancel)
+      }
+      Divider(
+        thickness = 18.dp,
+        color = Color.Transparent,
+        modifier = Modifier.padding(bottom = 16.dp)
+      )
+    }
+  }
+}
+
 @ExperimentalMaterialApi
 @Composable
 fun ProviderRow(providerInfo: ProviderInfo, onProviderSelected: (String) -> Unit) {
@@ -276,7 +346,7 @@
   onOptionSelected: (String) -> Unit,
   onCancel: () -> Unit,
   multiProvider: Boolean,
-  onMoreOptionSelected: () -> Unit,
+  onMoreOptionSelected: (String) -> Unit,
 ) {
   Card(
     backgroundColor = lightBackgroundColor,
@@ -318,7 +388,7 @@
           }
           if (multiProvider) {
             item {
-              MoreOptionRow(onSelect = onMoreOptionSelected)
+              MoreOptionRow(onSelect = { onMoreOptionSelected(providerInfo.name) })
             }
           }
         }
@@ -389,7 +459,7 @@
     shape = Shapes.large
   ) {
       Text(
-        text = stringResource(R.string.string_more_options),
+        text = stringResource(R.string.string_create_at_another_place),
         style = Typography.h6,
       )
   }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
index e42016d..15300de 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
@@ -52,7 +52,17 @@
     }
   }
 
-  fun onMoreOptionSelected() {
-    Log.d("Account Selector", "On more option selected")
+  fun onMoreOptionSelected(providerName: String) {
+    uiState = uiState.copy(
+        currentScreenState = CreateScreenState.MORE_OPTIONS_SELECTION,
+        selectedProvider = getProviderInfoByName(providerName)
+    )
+  }
+
+  fun onBackButtonSelected(providerName: String) {
+    uiState = uiState.copy(
+        currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
+        selectedProvider = getProviderInfoByName(providerName)
+    )
   }
 }
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 6669d6b..696ea4a 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -59,6 +59,10 @@
                 <action android:name="android.content.pm.action.CONFIRM_INSTALL" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter android:priority="1">
+                <action android:name="android.content.pm.action.CONFIRM_PRE_APPROVAL" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
         </activity>
 
         <activity android:name=".InstallStaging"
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index ac1a574f..bfab9be 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -59,7 +59,8 @@
         String callingAttributionTag = null;
 
         final boolean isSessionInstall =
-                PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
+                PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL.equals(intent.getAction())
+                        || PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
 
         // If the activity was started via a PackageInstaller session, we retrieve the calling
         // package from that session
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 20dc2cb..de76632 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -39,9 +39,11 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
+import android.graphics.drawable.BitmapDrawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Process;
@@ -151,7 +153,7 @@
     }
 
     /**
-     * Replace any dialog shown by the dialog with the one for the given {@link #createDialog id}.
+     * Replace any dialog shown by the dialog with the one for the given {@link #createDialog(int)}.
      *
      * @param id The dialog type to add
      */
@@ -296,6 +298,14 @@
                 ? RESULT_OK : RESULT_FIRST_USER, result);
     }
 
+    private static PackageInfo generateStubPackageInfo(String packageName) {
+        final PackageInfo info = new PackageInfo();
+        final ApplicationInfo aInfo = new ApplicationInfo();
+        info.applicationInfo = aInfo;
+        info.packageName = info.applicationInfo.packageName = packageName;
+        return info;
+    }
+
     @Override
     protected void onCreate(Bundle icicle) {
         if (mLocalLOGV) Log.i(TAG, "creating for user " + getUserId());
@@ -315,6 +325,7 @@
         mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
 
         final Intent intent = getIntent();
+        final String action = intent.getAction();
 
         mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
         mCallingAttributionTag = intent.getStringExtra(EXTRA_CALLING_ATTRIBUTION_TAG);
@@ -324,11 +335,11 @@
         mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
                 ? getPackageNameForUid(mOriginatingUid) : null;
 
-        final Uri packageUri;
-
-        if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
-            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
-            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
+        final Object packageSource;
+        if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(action)) {
+            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID,
+                    -1 /* defaultValue */);
+            final SessionInfo info = mInstaller.getSessionInfo(sessionId);
             if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
                 Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
                 finish();
@@ -336,18 +347,32 @@
             }
 
             mSessionId = sessionId;
-            packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
+            packageSource = Uri.fromFile(new File(info.resolvedBaseCodePath));
+            mOriginatingURI = null;
+            mReferrerURI = null;
+        } else if (PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL.equals(action)) {
+            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID,
+                    -1 /* defaultValue */);
+            final SessionInfo info = mInstaller.getSessionInfo(sessionId);
+            if (info == null || !info.isPreapprovalRequested) {
+                Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
+                finish();
+                return;
+            }
+
+            mSessionId = sessionId;
+            packageSource = info;
             mOriginatingURI = null;
             mReferrerURI = null;
         } else {
             mSessionId = -1;
-            packageUri = intent.getData();
+            packageSource = intent.getData();
             mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
             mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
         }
 
         // if there's nothing to do, quietly slip into the ether
-        if (packageUri == null) {
+        if (packageSource == null) {
             Log.w(TAG, "Unspecified source");
             setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
             finish();
@@ -359,7 +384,7 @@
             return;
         }
 
-        boolean wasSetUp = processPackageUri(packageUri);
+        final boolean wasSetUp = processAppSnippet(packageSource);
         if (mLocalLOGV) Log.i(TAG, "wasSetUp: " + wasSetUp);
 
         if (!wasSetUp) {
@@ -619,6 +644,39 @@
         return true;
     }
 
+    /**
+     * Use the SessionInfo and set up the installer for pre-commit install session.
+     *
+     * @param info The SessionInfo to compose
+     *
+     * @return {@code true} iff the installer could be set up
+     */
+    private boolean processSessionInfo(@NonNull SessionInfo info) {
+        mPkgInfo = generateStubPackageInfo(info.appPackageName);
+        mAppSnippet = new PackageUtil.AppSnippet(info.appLabel,
+                info.appIcon != null ? new BitmapDrawable(getResources(), info.appIcon)
+                        : getPackageManager().getDefaultActivityIcon());
+        return true;
+    }
+
+    /**
+     * Parse the Uri (post-commit install session) or use the SessionInfo (pre-commit install
+     * session) to set up the installer for this install.
+     *
+     * @param source The source of package URI or SessionInfo
+     *
+     * @return {@code true} iff the installer could be set up
+     */
+    private boolean processAppSnippet(@NonNull Object source) {
+        if (source instanceof Uri) {
+            return processPackageUri((Uri) source);
+        } else if (source instanceof SessionInfo) {
+            return processSessionInfo((SessionInfo) source);
+        }
+
+        return false;
+    }
+
     @Override
     public void onBackPressed() {
         if (mSessionId != -1) {
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index e583138..0a4972f 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -18,12 +18,13 @@
     package="com.android.settingslib.spa.gallery">
 
     <application
+        android:name=".GalleryApplication"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_label"
         android:supportsRtl="true"
         android:enableOnBackInvokedCallback="true">
         <activity
-            android:name=".MainActivity"
+            android:name=".GalleryMainActivity"
             android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryApplication.kt
similarity index 70%
copy from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
copy to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryApplication.kt
index 5e859ce..8c9d42c 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryApplication.kt
@@ -16,6 +16,12 @@
 
 package com.android.settingslib.spa.gallery
 
-import com.android.settingslib.spa.framework.BrowseActivity
+import android.app.Application
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 
-class MainActivity : BrowseActivity(GallerySpaEnvironment)
+class GalleryApplication : Application() {
+    override fun onCreate() {
+        super.onCreate()
+        SpaEnvironmentFactory.instance = GallerySpaEnvironment
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
index 332d5a8..23072a2 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
@@ -18,4 +18,4 @@
 
 import com.android.settingslib.spa.framework.DebugActivity
 
-class GalleryDebugActivity : DebugActivity(GallerySpaEnvironment)
+class GalleryDebugActivity : DebugActivity()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
index 5e04861..817c209f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
@@ -18,4 +18,4 @@
 
 import com.android.settingslib.spa.framework.EntryProvider
 
-class GalleryEntryProvider : EntryProvider(GallerySpaEnvironment)
+class GalleryEntryProvider : EntryProvider()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryMainActivity.kt
similarity index 92%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryMainActivity.kt
index 5e859ce..08a9bf5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryMainActivity.kt
@@ -18,4 +18,4 @@
 
 import com.android.settingslib.spa.framework.BrowseActivity
 
-class MainActivity : BrowseActivity(GallerySpaEnvironment)
+class GalleryMainActivity : BrowseActivity()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 33c4d77..aa457fe 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -87,7 +87,7 @@
         )
     }
 
-    override val browseActivityClass = MainActivity::class.java
+    override val browseActivityClass = GalleryMainActivity::class.java
 
     override val entryProviderAuthorities = "com.android.spa.gallery.provider"
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 8ca1c37..d87c31b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -30,7 +30,7 @@
 import androidx.navigation.compose.composable
 import androidx.navigation.compose.rememberNavController
 import com.android.settingslib.spa.R
-import com.android.settingslib.spa.framework.common.SpaEnvironment
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.compose.LocalNavController
 import com.android.settingslib.spa.framework.compose.NavControllerWrapperImpl
 import com.android.settingslib.spa.framework.compose.localNavController
@@ -50,8 +50,8 @@
  *   $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination HOME
  *   $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination ARGUMENT/bar/5
  */
-open class BrowseActivity(spaEnvironment: SpaEnvironment) : ComponentActivity() {
-    private val sppRepository by spaEnvironment.pageProviderRepository
+open class BrowseActivity : ComponentActivity() {
+    private val spaEnvironment get() = SpaEnvironmentFactory.instance
 
     override fun onCreate(savedInstanceState: Bundle?) {
         setTheme(R.style.Theme_SpaLib_DayNight)
@@ -67,6 +67,7 @@
 
     @Composable
     private fun MainContent() {
+        val sppRepository by spaEnvironment.pageProviderRepository
         val navController = rememberNavController()
         CompositionLocalProvider(navController.localNavController()) {
             NavHost(navController, NULL_PAGE_NAME) {
@@ -84,6 +85,7 @@
 
     @Composable
     private fun InitialDestinationNavigator() {
+        val sppRepository by spaEnvironment.pageProviderRepository
         val destinationNavigated = rememberSaveable { mutableStateOf(false) }
         if (destinationNavigated.value) return
         destinationNavigated.value = true
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
index ab7c0fe1..b28da06 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
@@ -37,7 +37,7 @@
 import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_HIGHLIGHT_ENTRY
 import com.android.settingslib.spa.framework.common.SettingsEntry
 import com.android.settingslib.spa.framework.common.SettingsPage
-import com.android.settingslib.spa.framework.common.SpaEnvironment
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.compose.localNavController
 import com.android.settingslib.spa.framework.compose.navigator
 import com.android.settingslib.spa.framework.compose.toState
@@ -63,8 +63,8 @@
  * For gallery, Activity = com.android.settingslib.spa.gallery/.GalleryDebugActivity
  * For SettingsGoogle, Activity = com.android.settings/.spa.SpaDebugActivity
  */
-open class DebugActivity(private val spaEnvironment: SpaEnvironment) : ComponentActivity() {
-    private val entryRepository by spaEnvironment.entryRepository
+open class DebugActivity : ComponentActivity() {
+    private val spaEnvironment get() = SpaEnvironmentFactory.instance
 
     override fun onCreate(savedInstanceState: Bundle?) {
         setTheme(R.style.Theme_SpaLib_DayNight)
@@ -128,6 +128,7 @@
 
     @Composable
     fun RootPage() {
+        val entryRepository by spaEnvironment.entryRepository
         val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() }
         val allEntry = remember { entryRepository.getAllEntries() }
         HomeScaffold(title = "Settings Debug") {
@@ -141,6 +142,7 @@
             })
             Preference(object : PreferenceModel {
                 override val title = "Query EntryProvider"
+                override val enabled = isEntryProviderAvailable().toState()
                 override val onClick = { displayDebugMessage() }
             })
         }
@@ -148,6 +150,7 @@
 
     @Composable
     fun AllPages() {
+        val entryRepository by spaEnvironment.entryRepository
         val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() }
         RegularScaffold(title = "All Pages (${allPageWithEntry.size})") {
             for (pageWithEntry in allPageWithEntry) {
@@ -164,6 +167,7 @@
 
     @Composable
     fun AllEntries() {
+        val entryRepository by spaEnvironment.entryRepository
         val allEntry = remember { entryRepository.getAllEntries() }
         RegularScaffold(title = "All Entries (${allEntry.size})") {
             EntryList(allEntry)
@@ -172,6 +176,7 @@
 
     @Composable
     fun OnePage(arguments: Bundle?) {
+        val entryRepository by spaEnvironment.entryRepository
         val id = arguments!!.getString(PARAM_NAME_PAGE_ID, "")
         val pageWithEntry = entryRepository.getPageWithEntry(id)!!
         RegularScaffold(title = "Page - ${pageWithEntry.page.displayName}") {
@@ -180,7 +185,7 @@
             Text(text = "Entry size: ${pageWithEntry.entries.size}")
             Preference(model = object : PreferenceModel {
                 override val title = "open page"
-                override val enabled = (!pageWithEntry.page.hasRuntimeParam()).toState()
+                override val enabled = isPageClickable(pageWithEntry.page).toState()
                 override val onClick = openPage(pageWithEntry.page)
             })
             EntryList(pageWithEntry.entries)
@@ -189,13 +194,14 @@
 
     @Composable
     fun OneEntry(arguments: Bundle?) {
+        val entryRepository by spaEnvironment.entryRepository
         val id = arguments!!.getString(PARAM_NAME_ENTRY_ID, "")
         val entry = entryRepository.getEntry(id)!!
         val entryContent = remember { entry.formatContent() }
         RegularScaffold(title = "Entry - ${entry.displayTitle()}") {
             Preference(model = object : PreferenceModel {
                 override val title = "open entry"
-                override val enabled = (!entry.containerPage().hasRuntimeParam()).toState()
+                override val enabled = isEntryClickable(entry).toState()
                 override val onClick = openEntry(entry)
             })
             Text(text = entryContent)
@@ -216,7 +222,7 @@
 
     @Composable
     private fun openPage(page: SettingsPage): (() -> Unit)? {
-        if (page.hasRuntimeParam()) return null
+        if (!isPageClickable(page)) return null
         val context = LocalContext.current
         val route = page.buildRoute()
         val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
@@ -230,7 +236,7 @@
 
     @Composable
     private fun openEntry(entry: SettingsEntry): (() -> Unit)? {
-        if (entry.containerPage().hasRuntimeParam()) return null
+        if (!isEntryClickable(entry)) return null
         val context = LocalContext.current
         val route = entry.containerPage().buildRoute()
         val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
@@ -242,4 +248,17 @@
             context.startActivity(intent)
         }
     }
+
+    private fun isEntryProviderAvailable(): Boolean {
+        return spaEnvironment.entryProviderAuthorities != null
+    }
+
+    private fun isPageClickable(page: SettingsPage): Boolean {
+        return spaEnvironment.browseActivityClass != null && !page.hasRuntimeParam()
+    }
+
+    private fun isEntryClickable(entry: SettingsEntry): Boolean {
+        return spaEnvironment.browseActivityClass != null &&
+            !entry.containerPage().hasRuntimeParam()
+    }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
index 50157fc..532f63b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
@@ -30,7 +30,7 @@
 import android.util.Log
 import com.android.settingslib.spa.framework.common.SettingsEntry
 import com.android.settingslib.spa.framework.common.SettingsPage
-import com.android.settingslib.spa.framework.common.SpaEnvironment
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 
 private const val TAG = "EntryProvider"
 
@@ -49,9 +49,8 @@
  *   $ adb shell content query --uri content://<AuthorityPath>/search_static
  *   $ adb shell content query --uri content://<AuthorityPath>/search_dynamic
  */
-open class EntryProvider(spaEnvironment: SpaEnvironment) : ContentProvider() {
-    private val entryRepository by spaEnvironment.entryRepository
-    private val browseActivityClass = spaEnvironment.browseActivityClass
+open class EntryProvider : ContentProvider() {
+    private val spaEnvironment get() = SpaEnvironmentFactory.instance
 
     /**
      * Enum to define all column names in provider.
@@ -221,6 +220,7 @@
     }
 
     private fun queryPageDebug(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.PAGE_DEBUG_QUERY.getColumns())
         for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
             val command = createBrowsePageAdbCommand(pageWithEntry.page)
@@ -232,6 +232,7 @@
     }
 
     private fun queryEntryDebug(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.ENTRY_DEBUG_QUERY.getColumns())
         for (entry in entryRepository.getAllEntries()) {
             val command = createBrowsePageAdbCommand(entry.containerPage(), entry.id)
@@ -243,6 +244,7 @@
     }
 
     private fun queryPageInfo(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.PAGE_INFO_QUERY.getColumns())
         for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
             val page = pageWithEntry.page
@@ -261,6 +263,7 @@
     }
 
     private fun queryEntryInfo(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.ENTRY_INFO_QUERY.getColumns())
         for (entry in entryRepository.getAllEntries()) {
             cursor.newRow()
@@ -276,6 +279,7 @@
     }
 
     private fun querySearchSitemap(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.SEARCH_SITEMAP_QUERY.getColumns())
         for (entry in entryRepository.getAllEntries()) {
             if (!entry.isAllowSearch) continue
@@ -287,6 +291,7 @@
     }
 
     private fun querySearchStaticData(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.SEARCH_STATIC_DATA_QUERY.getColumns())
         for (entry in entryRepository.getAllEntries()) {
             if (!entry.isAllowSearch || entry.isSearchDataDynamic) continue
@@ -296,6 +301,7 @@
     }
 
     private fun querySearchDynamicData(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.SEARCH_DYNAMIC_DATA_QUERY.getColumns())
         for (entry in entryRepository.getAllEntries()) {
             if (!entry.isAllowSearch || !entry.isSearchDataDynamic) continue
@@ -317,26 +323,31 @@
     }
 
     private fun createBrowsePageIntent(page: SettingsPage, entryId: String? = null): Intent {
-        if (context == null || page.hasRuntimeParam())
-            return Intent()
-
-        return Intent().setComponent(ComponentName(context!!, browseActivityClass)).apply {
-            putExtra(BrowseActivity.KEY_DESTINATION, page.buildRoute())
-            if (entryId != null) {
-                putExtra(BrowseActivity.KEY_HIGHLIGHT_ENTRY, entryId)
+        if (!isPageBrowsable(page)) return Intent()
+        return Intent().setComponent(ComponentName(context!!, spaEnvironment.browseActivityClass!!))
+            .apply {
+                putExtra(BrowseActivity.KEY_DESTINATION, page.buildRoute())
+                if (entryId != null) {
+                    putExtra(BrowseActivity.KEY_HIGHLIGHT_ENTRY, entryId)
+                }
             }
-        }
     }
 
     private fun createBrowsePageAdbCommand(page: SettingsPage, entryId: String? = null): String? {
-        if (context == null || page.hasRuntimeParam()) return null
+        if (!isPageBrowsable(page)) return null
         val packageName = context!!.packageName
-        val activityName = browseActivityClass.name.replace(packageName, "")
+        val activityName = spaEnvironment.browseActivityClass!!.name.replace(packageName, "")
         val destinationParam = " -e ${BrowseActivity.KEY_DESTINATION} ${page.buildRoute()}"
         val highlightParam =
             if (entryId != null) " -e ${BrowseActivity.KEY_HIGHLIGHT_ENTRY} $entryId" else ""
         return "adb shell am start -n $packageName/$activityName$destinationParam$highlightParam"
     }
+
+    private fun isPageBrowsable(page: SettingsPage): Boolean {
+        return context != null &&
+            spaEnvironment.browseActivityClass != null &&
+            !page.hasRuntimeParam()
+    }
 }
 
 fun EntryProvider.QueryEnum.getColumns(): Array<String> {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
index 111555b..3885025 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -18,12 +18,28 @@
 
 import android.app.Activity
 
+object SpaEnvironmentFactory {
+    private var spaEnvironment: SpaEnvironment? = null
+
+    var instance: SpaEnvironment
+        get() {
+            if (spaEnvironment == null)
+                throw UnsupportedOperationException("Spa environment is not set")
+            return spaEnvironment!!
+        }
+        set(env: SpaEnvironment) {
+            if (spaEnvironment != null)
+                throw UnsupportedOperationException("Spa environment is already set")
+            spaEnvironment = env
+        }
+}
+
 abstract class SpaEnvironment {
     abstract val pageProviderRepository: Lazy<SettingsPageProviderRepository>
 
     val entryRepository = lazy { SettingsEntryRepository(pageProviderRepository.value) }
 
-    abstract val browseActivityClass: Class<out Activity>
+    open val browseActivityClass: Class<out Activity>? = null
 
     open val entryProviderAuthorities: String? = null
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index fe337d267..8e33ca3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -106,13 +106,20 @@
             + "license content #1\n"
             + "</pre><!-- license-text -->\n"
             + "</td></tr><!-- same-license -->\n"
-            + "</table></body></html>\n";
+            + "</table>\n"
+            + "<div class=\"path-counts\"><table>\n"
+            + "  <tr><th>Path prefix</th><th>Count</th></tr>\n\n"
+            + "  <tr><td>file0</td><td>1</td></tr>\n"
+            + "  <tr><td>file1</td><td>1</td></tr>\n"
+            + "</table></div>\n\n"
+            + "</body></html>\n";
 
     private static final String HTML_NEW_BODY_STRING =
             "<strong>Libraries</strong>\n"
             + "<ul class=\"libraries\">\n"
             + "<li><a href=\"#id0\">libA</a></li>\n"
             + "<li><a href=\"#id1\">libB</a></li>\n"
+            + "<li><a href=\"#id0\">libC</a></li>\n"
             + "</ul>\n"
             + "<strong>Files</strong>\n"
             + "<ul class=\"files\">\n"
@@ -146,7 +153,14 @@
             + "license content #1\n"
             + "</pre><!-- license-text -->\n"
             + "</td></tr><!-- same-license -->\n"
-            + "</table></body></html>\n";
+            + "</table>\n"
+            + "<div class=\"path-counts\"><table>\n"
+            + "  <tr><th>Path prefix</th><th>Count</th></tr>\n\n"
+            + "  <tr><td>file0</td><td>1</td></tr>\n"
+            + "  <tr><td>file1</td><td>1</td></tr>\n"
+            + "  <tr><td>file2</td><td>1</td></tr>\n"
+            + "</table></div>\n\n"
+            + "</body></html>\n";
 
     private static final String EXPECTED_OLD_HTML_STRING = HTML_HEAD_STRING + HTML_OLD_BODY_STRING;
 
@@ -263,7 +277,7 @@
         Map<String, Set<String>> toOne = new HashMap<>();
 
         toBoth.put("", new HashSet<String>(Arrays.asList("0", "1")));
-        toOne.put("", new HashSet<String>(Arrays.asList("0", "1")));
+        toOne.put("", new HashSet<String>(Arrays.asList("0")));
 
         fileNameToLibraryToContentIdMap.put("/file0", toBoth);
         fileNameToLibraryToContentIdMap.put("/file1", toOne);
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fa3ed21..01c9ac1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1582,4 +1582,9 @@
     <dimen name="dream_overlay_status_bar_ambient_text_shadow_dx">0.5dp</dimen>
     <dimen name="dream_overlay_status_bar_ambient_text_shadow_dy">0.5dp</dimen>
     <dimen name="dream_overlay_status_bar_ambient_text_shadow_radius">2dp</dimen>
+
+    <!-- Default device corner radius, used for assist UI -->
+    <dimen name="config_rounded_mask_size">0px</dimen>
+    <dimen name="config_rounded_mask_size_top">0px</dimen>
+    <dimen name="config_rounded_mask_size_bottom">0px</dimen>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 2111df5..647dd47 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -27,6 +27,8 @@
 import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -233,17 +235,14 @@
     @ViewDebug.ExportedProperty(category="recents")
     public boolean isLocked;
 
+    public Point positionInParent;
+
+    public Rect appBounds;
+
     // Last snapshot data, only used for recent tasks
     public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
             new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData();
 
-    /**
-     * Indicates that this task for the desktop tile in recents.
-     *
-     * Used when desktop mode feature is enabled.
-     */
-    public boolean desktopTile;
-
     public Task() {
         // Do nothing
     }
@@ -274,7 +273,8 @@
         this(other.key, other.colorPrimary, other.colorBackground, other.isDockable,
                 other.isLocked, other.taskDescription, other.topActivity);
         lastSnapshotData.set(other.lastSnapshotData);
-        desktopTile = other.desktopTile;
+        positionInParent = other.positionInParent;
+        appBounds = other.appBounds;
     }
 
     /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 22bffda..6087655 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -132,8 +132,11 @@
             mMainThreadHandler.postAtFrontOfQueue(() -> {
                 // If the screen rotation changes while locked, potentially update lock to flow with
                 // new screen rotation and hide any showing suggestions.
-                if (isRotationLocked()) {
-                    if (shouldOverrideUserLockPrefs(rotation)) {
+                boolean rotationLocked = isRotationLocked();
+                // The isVisible check makes the rotation button disappear when we are not locked
+                // (e.g. for tabletop auto-rotate).
+                if (rotationLocked || mRotationButton.isVisible()) {
+                    if (shouldOverrideUserLockPrefs(rotation) && rotationLocked) {
                         setRotationLockedAtAngle(rotation);
                     }
                     setRotateSuggestionButtonState(false /* visible */, true /* forced */);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index cd9f851..80120a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -3164,14 +3164,7 @@
      * Whether the keyguard is showing and not occluded.
      */
     public boolean isKeyguardVisible() {
-        return isKeyguardShowing() && !mKeyguardOccluded;
-    }
-
-    /**
-     * Whether the keyguard is showing. It may still be occluded and not visible.
-     */
-    public boolean isKeyguardShowing() {
-        return mKeyguardShowing;
+        return mKeyguardShowing && !mKeyguardOccluded;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 3ea8826..90f0446 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -94,11 +94,6 @@
     void setOccluded(boolean occluded, boolean animate);
 
     /**
-     * @return Whether the keyguard is showing
-     */
-    boolean isShowing();
-
-    /**
      * Dismisses the keyguard by going to the next screen or making it gone.
      */
     void dismissAndCollapse();
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DisplayUtils.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DisplayUtils.java
index 33e6ca4..9b441ad 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DisplayUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DisplayUtils.java
@@ -21,6 +21,8 @@
 import android.view.Display;
 import android.view.Surface;
 
+import com.android.systemui.R;
+
 /**
  * Utility class for determining screen and corner dimensions.
  */
@@ -82,17 +84,13 @@
      * where the curve ends), in pixels.
      */
     public static int getCornerRadiusBottom(Context context) {
-        int radius = 0;
-
-        int resourceId = context.getResources().getIdentifier("config_rounded_mask_size_bottom",
-                "dimen", "com.android.systemui");
-        if (resourceId > 0) {
-            radius = context.getResources().getDimensionPixelSize(resourceId);
-        }
+        int radius = context.getResources().getDimensionPixelSize(
+                R.dimen.config_rounded_mask_size_bottom);
 
         if (radius == 0) {
             radius = getCornerRadiusDefault(context);
         }
+
         return radius;
     }
 
@@ -101,28 +99,17 @@
      * the curve ends), in pixels.
      */
     public static int getCornerRadiusTop(Context context) {
-        int radius = 0;
-
-        int resourceId = context.getResources().getIdentifier("config_rounded_mask_size_top",
-                "dimen", "com.android.systemui");
-        if (resourceId > 0) {
-            radius = context.getResources().getDimensionPixelSize(resourceId);
-        }
+        int radius = context.getResources().getDimensionPixelSize(
+                R.dimen.config_rounded_mask_size_top);
 
         if (radius == 0) {
             radius = getCornerRadiusDefault(context);
         }
+
         return radius;
     }
 
     private static int getCornerRadiusDefault(Context context) {
-        int radius = 0;
-
-        int resourceId = context.getResources().getIdentifier("config_rounded_mask_size",
-                "dimen", "com.android.systemui");
-        if (resourceId > 0) {
-            radius = context.getResources().getDimensionPixelSize(resourceId);
-        }
-        return radius;
+        return context.getResources().getDimensionPixelSize(R.dimen.config_rounded_mask_size);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 2578df3..0f5a99c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -245,7 +245,7 @@
                     mAcquiredReceived = true;
                     final UdfpsView view = mOverlay.getOverlayView();
                     if (view != null) {
-                        view.unconfigureDisplay();
+                        unconfigureDisplay(view);
                     }
                     if (acquiredGood) {
                         mOverlay.onAcquiredGood();
@@ -737,6 +737,19 @@
 
         mOverlay = null;
         mOrientationListener.disable();
+
+    }
+
+    private void unconfigureDisplay(@NonNull UdfpsView view) {
+        if (view.isDisplayConfigured()) {
+            view.unconfigureDisplay();
+
+            if (mCancelAodTimeoutAction != null) {
+                mCancelAodTimeoutAction.run();
+                mCancelAodTimeoutAction = null;
+            }
+            mIsAodInterruptActive = false;
+        }
     }
 
     /**
@@ -812,12 +825,12 @@
      * sensors, this can result in illumination persisting for longer than necessary.
      */
     void onCancelUdfps() {
-        if (mOverlay != null && mOverlay.getOverlayView() != null) {
-            onFingerUp(mOverlay.getRequestId(), mOverlay.getOverlayView());
-        }
         if (!mIsAodInterruptActive) {
             return;
         }
+        if (mOverlay != null && mOverlay.getOverlayView() != null) {
+            onFingerUp(mOverlay.getRequestId(), mOverlay.getOverlayView());
+        }
         if (mCancelAodTimeoutAction != null) {
             mCancelAodTimeoutAction.run();
             mCancelAodTimeoutAction = null;
@@ -911,15 +924,8 @@
             }
         }
         mOnFingerDown = false;
-        if (view.isDisplayConfigured()) {
-            view.unconfigureDisplay();
-        }
+        unconfigureDisplay(view);
 
-        if (mCancelAodTimeoutAction != null) {
-            mCancelAodTimeoutAction.run();
-            mCancelAodTimeoutAction = null;
-        }
-        mIsAodInterruptActive = false;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 934aedf..4d7f89d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -219,7 +219,7 @@
                 mView.animateInUdfpsBouncer(null);
             }
 
-            if (mKeyguardViewManager.isOccluded()) {
+            if (mKeyguardStateController.isOccluded()) {
                 mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 696fc72..d1b7368 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -64,29 +64,26 @@
     private final Executor mExecutor;
     // A controller for the dream overlay container view (which contains both the status bar and the
     // content area).
-    private final DreamOverlayContainerViewController mDreamOverlayContainerViewController;
+    private DreamOverlayContainerViewController mDreamOverlayContainerViewController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Nullable
     private final ComponentName mLowLightDreamComponent;
     private final UiEventLogger mUiEventLogger;
+    private final WindowManager mWindowManager;
 
     // A reference to the {@link Window} used to hold the dream overlay.
     private Window mWindow;
 
-    // True if the service has been destroyed.
-    private boolean mDestroyed;
+    // True if a dream has bound to the service and dream overlay service has started.
+    private boolean mStarted = false;
 
-    private final Complication.Host mHost = new Complication.Host() {
-        @Override
-        public void requestExitDream() {
-            mExecutor.execute(DreamOverlayService.this::requestExit);
-        }
-    };
+    // True if the service has been destroyed.
+    private boolean mDestroyed = false;
+
+    private final DreamOverlayComponent mDreamOverlayComponent;
 
     private final LifecycleRegistry mLifecycleRegistry;
 
-    private ViewModelStore mViewModelStore = new ViewModelStore();
-
     private DreamOverlayTouchMonitor mDreamOverlayTouchMonitor;
 
     private final KeyguardUpdateMonitorCallback mKeyguardCallback =
@@ -103,7 +100,7 @@
                 }
             };
 
-    private DreamOverlayStateController mStateController;
+    private final DreamOverlayStateController mStateController;
 
     @VisibleForTesting
     public enum DreamOverlayEvent implements UiEventLogger.UiEventEnum {
@@ -128,6 +125,7 @@
     public DreamOverlayService(
             Context context,
             @Main Executor executor,
+            WindowManager windowManager,
             DreamOverlayComponent.Factory dreamOverlayComponentFactory,
             DreamOverlayStateController stateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -136,19 +134,19 @@
                     ComponentName lowLightDreamComponent) {
         mContext = context;
         mExecutor = executor;
+        mWindowManager = windowManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLowLightDreamComponent = lowLightDreamComponent;
         mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
         mStateController = stateController;
         mUiEventLogger = uiEventLogger;
 
-        final DreamOverlayComponent component =
-                dreamOverlayComponentFactory.create(mViewModelStore, mHost);
-        mDreamOverlayContainerViewController = component.getDreamOverlayContainerViewController();
+        final ViewModelStore viewModelStore = new ViewModelStore();
+        final Complication.Host host =
+                () -> mExecutor.execute(DreamOverlayService.this::requestExit);
+        mDreamOverlayComponent = dreamOverlayComponentFactory.create(viewModelStore, host);
+        mLifecycleRegistry = mDreamOverlayComponent.getLifecycleRegistry();
         setCurrentState(Lifecycle.State.CREATED);
-        mLifecycleRegistry = component.getLifecycleRegistry();
-        mDreamOverlayTouchMonitor = component.getDreamOverlayTouchMonitor();
-        mDreamOverlayTouchMonitor.init();
     }
 
     private void setCurrentState(Lifecycle.State state) {
@@ -159,34 +157,48 @@
     public void onDestroy() {
         mKeyguardUpdateMonitor.removeCallback(mKeyguardCallback);
         setCurrentState(Lifecycle.State.DESTROYED);
-        final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        if (mWindow != null) {
-            windowManager.removeView(mWindow.getDecorView());
-        }
-        mStateController.setOverlayActive(false);
-        mStateController.setLowLightActive(false);
+
+        resetCurrentDreamOverlay();
+
         mDestroyed = true;
         super.onDestroy();
     }
 
     @Override
     public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
-        mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
         setCurrentState(Lifecycle.State.STARTED);
-        final ComponentName dreamComponent = getDreamComponent();
-        mStateController.setLowLightActive(
-                dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent));
+
         mExecutor.execute(() -> {
+            mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
+
             if (mDestroyed) {
                 // The task could still be executed after the service has been destroyed. Bail if
                 // that is the case.
                 return;
             }
+
+            if (mStarted) {
+                // Reset the current dream overlay before starting a new one. This can happen
+                // when two dreams overlap (briefly, for a smoother dream transition) and both
+                // dreams are bound to the dream overlay service.
+                resetCurrentDreamOverlay();
+            }
+
+            mDreamOverlayContainerViewController =
+                    mDreamOverlayComponent.getDreamOverlayContainerViewController();
+            mDreamOverlayTouchMonitor = mDreamOverlayComponent.getDreamOverlayTouchMonitor();
+            mDreamOverlayTouchMonitor.init();
+
             mStateController.setShouldShowComplications(shouldShowComplications());
             addOverlayWindowLocked(layoutParams);
             setCurrentState(Lifecycle.State.RESUMED);
             mStateController.setOverlayActive(true);
+            final ComponentName dreamComponent = getDreamComponent();
+            mStateController.setLowLightActive(
+                    dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent));
             mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);
+
+            mStarted = true;
         });
     }
 
@@ -222,8 +234,7 @@
         removeContainerViewFromParent();
         mWindow.setContentView(mDreamOverlayContainerViewController.getContainerView());
 
-        final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
+        mWindowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
     }
 
     private void removeContainerViewFromParent() {
@@ -238,4 +249,18 @@
         Log.w(TAG, "Removing dream overlay container view parent!");
         parentView.removeView(containerView);
     }
+
+    private void resetCurrentDreamOverlay() {
+        if (mStarted && mWindow != null) {
+            mWindowManager.removeView(mWindow.getDecorView());
+        }
+
+        mStateController.setOverlayActive(false);
+        mStateController.setLowLightActive(false);
+
+        mDreamOverlayContainerViewController = null;
+        mDreamOverlayTouchMonitor = null;
+
+        mStarted = false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 8e2ac06..3dbadb0 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -235,7 +235,7 @@
     // 1100 - windowing
     @Keep
     public static final SysPropBooleanFlag WM_ENABLE_SHELL_TRANSITIONS =
-            new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", true);
+            new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false);
 
     /**
      * b/170163464: animate bubbles expanded view collapse with home gesture
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index ad8c688..c4eac1c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -662,7 +662,7 @@
     }
 
     override fun onKeyguardDismissAmountChanged() {
-        if (keyguardViewController.isShowing && !playingCannedUnlockAnimation) {
+        if (keyguardStateController.isShowing && !playingCannedUnlockAnimation) {
             showOrHideSurfaceIfDismissAmountThresholdsReached()
 
             // If the surface is visible or it's about to be, start updating its appearance to
@@ -721,7 +721,7 @@
      */
     private fun finishKeyguardExitRemoteAnimationIfReachThreshold() {
         // no-op if keyguard is not showing or animation is not enabled.
-        if (!keyguardViewController.isShowing) {
+        if (!keyguardStateController.isShowing) {
             return
         }
 
@@ -844,7 +844,7 @@
      * animation.
      */
     fun hideKeyguardViewAfterRemoteAnimation() {
-        if (keyguardViewController.isShowing) {
+        if (keyguardStateController.isShowing) {
             // Hide the keyguard, with no fade out since we animated it away during the unlock.
 
             keyguardViewController.hide(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7155acf..aee70ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1875,7 +1875,7 @@
         // if the keyguard is already showing, don't bother. check flags in both files
         // to account for the hiding animation which results in a delay and discrepancy
         // between flags
-        if (mShowing && mKeyguardViewControllerLazy.get().isShowing()) {
+        if (mShowing && mKeyguardStateController.isShowing()) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
             resetStateLocked();
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index da9fefa..33021e3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -48,7 +48,6 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.Dumpable;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -61,6 +60,7 @@
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -90,7 +90,7 @@
     private final AccessibilityManager mAccessibilityManager;
     private final Lazy<AssistManager> mAssistManagerLazy;
     private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
-    private final KeyguardViewController mKeyguardViewController;
+    private final KeyguardStateController mKeyguardStateController;
     private final UserTracker mUserTracker;
     private final SystemActions mSystemActions;
     private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@@ -125,7 +125,7 @@
             OverviewProxyService overviewProxyService,
             Lazy<AssistManager> assistManagerLazy,
             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
-            KeyguardViewController keyguardViewController,
+            KeyguardStateController keyguardStateController,
             NavigationModeController navigationModeController,
             UserTracker userTracker,
             DumpManager dumpManager) {
@@ -134,7 +134,7 @@
         mAccessibilityManager = accessibilityManager;
         mAssistManagerLazy = assistManagerLazy;
         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
-        mKeyguardViewController = keyguardViewController;
+        mKeyguardStateController = keyguardStateController;
         mUserTracker = userTracker;
         mSystemActions = systemActions;
         accessibilityManager.addAccessibilityServicesStateChangeListener(this);
@@ -326,7 +326,7 @@
             shadeWindowView =
                     mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
         }
-        boolean isKeyguardShowing = mKeyguardViewController.isShowing();
+        boolean isKeyguardShowing = mKeyguardStateController.isShowing();
         boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
                 && shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
         return imeVisibleOnShade
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c337dea..6d76c17 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -4287,7 +4287,7 @@
                 }
 
                 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyExpanded()
-                        && mStatusBarKeyguardViewManager.isShowing()) {
+                        && mKeyguardStateController.isShowing()) {
                     mStatusBarKeyguardViewManager.updateKeyguardPosition(event.getX());
                 }
 
@@ -4405,7 +4405,7 @@
         }
         mSysUiState.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
                         isFullyExpanded() && !isInSettings())
-                .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, isInSettings())
+                .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, isFullyExpanded() && isInSettings())
                 .commitUpdate(mDisplayId);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
index 1be4c04..b5c7ef5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification;
 
-import android.annotation.Nullable;
 import android.util.ArraySet;
 
 import androidx.annotation.VisibleForTesting;
@@ -25,7 +24,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import javax.inject.Inject;
@@ -43,7 +41,6 @@
 
     private boolean mLastDynamicUnlocked;
     private boolean mCacheInvalid;
-    @Nullable private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
     @Inject
     DynamicPrivacyController(NotificationLockscreenUserManager notificationLockscreenUserManager,
@@ -100,7 +97,7 @@
      * contents aren't revealed yet?
      */
     public boolean isInLockedDownShade() {
-        if (!isStatusBarKeyguardShowing() || !mKeyguardStateController.isMethodSecure()) {
+        if (!mKeyguardStateController.isShowing() || !mKeyguardStateController.isMethodSecure()) {
             return false;
         }
         int state = mStateController.getState();
@@ -113,16 +110,7 @@
         return true;
     }
 
-    private boolean isStatusBarKeyguardShowing() {
-        return mStatusBarKeyguardViewManager != null && mStatusBarKeyguardViewManager.isShowing();
-    }
-
-    public void setStatusBarKeyguardViewManager(
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
-        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
-    }
-
     public interface Listener {
         void onDynamicPrivacyChanged();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index a7719d3..e71d80c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -18,6 +18,8 @@
 
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
 
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+
 import android.os.SystemClock;
 import android.service.notification.NotificationStats;
 
@@ -70,6 +72,8 @@
             dismissalSurface = NotificationStats.DISMISSAL_PEEK;
         } else if (mStatusBarStateController.isDozing()) {
             dismissalSurface = NotificationStats.DISMISSAL_AOD;
+        } else if (mStatusBarStateController.getState() == KEYGUARD) {
+            dismissalSurface = NotificationStats.DISMISSAL_LOCKSCREEN;
         }
         return new DismissedByUserStats(
                 dismissalSurface,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ce465bc..2719dd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -44,7 +44,8 @@
 import javax.inject.Inject;
 
 /**
- * A global state to track all input states for the algorithm.
+ * Global state to track all input states for
+ * {@link com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm}.
  */
 @SysUISingleton
 public class AmbientState implements Dumpable {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 4576a64..9900e41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -150,7 +150,6 @@
     private final KeyguardBypassController mKeyguardBypassController;
     private PowerManager.WakeLock mWakeLock;
     private final KeyguardUpdateMonitor mUpdateMonitor;
-    private final DozeParameters mDozeParameters;
     private final KeyguardStateController mKeyguardStateController;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final SessionTracker mSessionTracker;
@@ -262,7 +261,7 @@
             KeyguardStateController keyguardStateController, Handler handler,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             @Main Resources resources,
-            KeyguardBypassController keyguardBypassController, DozeParameters dozeParameters,
+            KeyguardBypassController keyguardBypassController,
             MetricsLogger metricsLogger, DumpManager dumpManager,
             PowerManager powerManager,
             NotificationMediaManager notificationMediaManager,
@@ -276,7 +275,6 @@
             VibratorHelper vibrator) {
         mPowerManager = powerManager;
         mUpdateMonitor = keyguardUpdateMonitor;
-        mDozeParameters = dozeParameters;
         mUpdateMonitor.registerCallback(this);
         mMediaManager = notificationMediaManager;
         mLatencyTracker = latencyTracker;
@@ -522,7 +520,7 @@
         boolean deviceDreaming = mUpdateMonitor.isDreaming();
 
         if (!mUpdateMonitor.isDeviceInteractive()) {
-            if (!mKeyguardViewController.isShowing()
+            if (!mKeyguardStateController.isShowing()
                     && !mScreenOffAnimationController.isKeyguardShowDelayed()) {
                 if (mKeyguardStateController.isUnlocked()) {
                     return MODE_WAKE_AND_UNLOCK;
@@ -539,7 +537,7 @@
         if (unlockingAllowed && deviceDreaming) {
             return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
         }
-        if (mKeyguardViewController.isShowing()) {
+        if (mKeyguardStateController.isShowing()) {
             if (mKeyguardViewController.bouncerIsOrWillBeShowing() && unlockingAllowed) {
                 return MODE_DISMISS_BOUNCER;
             } else if (unlockingAllowed) {
@@ -558,7 +556,7 @@
         boolean bypass = mKeyguardBypassController.getBypassEnabled()
                 || mAuthController.isUdfpsFingerDown();
         if (!mUpdateMonitor.isDeviceInteractive()) {
-            if (!mKeyguardViewController.isShowing()) {
+            if (!mKeyguardStateController.isShowing()) {
                 return bypass ? MODE_WAKE_AND_UNLOCK : MODE_ONLY_WAKE;
             } else if (!unlockingAllowed) {
                 return bypass ? MODE_SHOW_BOUNCER : MODE_NONE;
@@ -582,7 +580,7 @@
         if (unlockingAllowed && mKeyguardStateController.isOccluded()) {
             return MODE_UNLOCK_COLLAPSING;
         }
-        if (mKeyguardViewController.isShowing()) {
+        if (mKeyguardStateController.isShowing()) {
             if ((mKeyguardViewController.bouncerIsOrWillBeShowing()
                     || mKeyguardBypassController.getAltBouncerShowing()) && unlockingAllowed) {
                 return MODE_DISMISS_BOUNCER;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index e30bc68..d6fadca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -363,7 +363,7 @@
             mKeyguardUpdateMonitor.onCameraLaunched();
         }
 
-        if (!mStatusBarKeyguardViewManager.isShowing()) {
+        if (!mKeyguardStateController.isShowing()) {
             final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
             mCentralSurfaces.startActivityDismissingKeyguard(cameraIntent,
                     false /* onlyProvisioned */, true /* dismissShade */,
@@ -420,7 +420,7 @@
         // TODO(b/169087248) Possibly add haptics here for emergency action. Currently disabled for
         // app-side haptic experimentation.
 
-        if (!mStatusBarKeyguardViewManager.isShowing()) {
+        if (!mKeyguardStateController.isShowing()) {
             mCentralSurfaces.startActivityDismissingKeyguard(emergencyIntent,
                     false /* onlyProvisioned */, true /* dismissShade */,
                     true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index cc15c82..b6e658f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -459,7 +459,6 @@
     private final KeyguardStateController mKeyguardStateController;
     private final HeadsUpManagerPhone mHeadsUpManager;
     private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
-    private final DynamicPrivacyController mDynamicPrivacyController;
     private final FalsingCollector mFalsingCollector;
     private final FalsingManager mFalsingManager;
     private final BroadcastDispatcher mBroadcastDispatcher;
@@ -762,7 +761,6 @@
         mHeadsUpManager = headsUpManagerPhone;
         mKeyguardIndicationController = keyguardIndicationController;
         mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
-        mDynamicPrivacyController = dynamicPrivacyController;
         mFalsingCollector = falsingCollector;
         mFalsingManager = falsingManager;
         mBroadcastDispatcher = broadcastDispatcher;
@@ -1553,7 +1551,6 @@
                 .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
         mRemoteInputManager.addControllerCallback(mStatusBarKeyguardViewManager);
-        mDynamicPrivacyController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
 
         mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
         mMediaManager.setBiometricUnlockController(mBiometricUnlockController);
@@ -2062,7 +2059,7 @@
 
         // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
         // the bouncer appear animation.
-        if (!mStatusBarKeyguardViewManager.isShowing()) {
+        if (!mKeyguardStateController.isShowing()) {
             WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
         }
     }
@@ -2499,8 +2496,8 @@
         };
         // Do not deferKeyguard when occluded because, when keyguard is occluded,
         // we do not launch the activity until keyguard is done.
-        boolean occluded = mStatusBarKeyguardViewManager.isShowing()
-                && mStatusBarKeyguardViewManager.isOccluded();
+        boolean occluded = mKeyguardStateController.isShowing()
+                && mKeyguardStateController.isOccluded();
         boolean deferred = !occluded;
         executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly,
                 willLaunchResolverActivity, deferred /* deferred */, animate);
@@ -2570,8 +2567,8 @@
             @Override
             public boolean onDismiss() {
                 if (runnable != null) {
-                    if (mStatusBarKeyguardViewManager.isShowing()
-                            && mStatusBarKeyguardViewManager.isOccluded()) {
+                    if (mKeyguardStateController.isShowing()
+                            && mKeyguardStateController.isOccluded()) {
                         mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
                     } else {
                         mMainExecutor.execute(runnable);
@@ -2665,7 +2662,7 @@
 
     private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen,
             boolean afterKeyguardGone) {
-        if (mStatusBarKeyguardViewManager.isShowing() && requiresShadeOpen) {
+        if (mKeyguardStateController.isShowing() && requiresShadeOpen) {
             mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
         }
         dismissKeyguardThenExecute(action, null /* cancelAction */,
@@ -2689,7 +2686,7 @@
             mBiometricUnlockController.startWakeAndUnlock(
                     BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
         }
-        if (mStatusBarKeyguardViewManager.isShowing()) {
+        if (mKeyguardStateController.isShowing()) {
             mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
                     afterKeyguardGone);
         } else {
@@ -2825,8 +2822,8 @@
     }
 
     private void logStateToEventlog() {
-        boolean isShowing = mStatusBarKeyguardViewManager.isShowing();
-        boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded();
+        boolean isShowing = mKeyguardStateController.isShowing();
+        boolean isOccluded = mKeyguardStateController.isOccluded();
         boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
         boolean isSecure = mKeyguardStateController.isMethodSecure();
         boolean unlocked = mKeyguardStateController.canDismissLockScreen();
@@ -3222,18 +3219,17 @@
         Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
         Trace.beginSection("CentralSurfaces#updateDozingState");
 
-        boolean visibleNotOccluded = mStatusBarKeyguardViewManager.isShowing()
-                && !mStatusBarKeyguardViewManager.isOccluded();
+        boolean keyguardVisible = mKeyguardStateController.isVisible();
         // If we're dozing and we'll be animating the screen off, the keyguard isn't currently
         // visible but will be shortly for the animation, so we should proceed as if it's visible.
-        boolean visibleNotOccludedOrWillBe =
-                visibleNotOccluded || (mDozing && mDozeParameters.shouldDelayKeyguardShow());
+        boolean keyguardVisibleOrWillBe =
+                keyguardVisible || (mDozing && mDozeParameters.shouldDelayKeyguardShow());
 
         boolean wakeAndUnlock = mBiometricUnlockController.getMode()
                 == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
         boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock)
                 || (mDozing && mDozeParameters.shouldControlScreenOff()
-                && visibleNotOccludedOrWillBe);
+                && keyguardVisibleOrWillBe);
 
         mNotificationPanelViewController.setDozing(mDozing, animate);
         updateQsExpansionEnabled();
@@ -3915,11 +3911,7 @@
 
     @Override
     public boolean isKeyguardShowing() {
-        if (mStatusBarKeyguardViewManager == null) {
-            Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true");
-            return true;
-        }
-        return mStatusBarKeyguardViewManager.isShowing();
+        return mKeyguardStateController.isShowing();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index d4d510f..5f5ec68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -230,8 +230,6 @@
     private View mNotificationContainer;
 
     @Nullable protected KeyguardBouncer mBouncer;
-    protected boolean mShowing;
-    protected boolean mOccluded;
     protected boolean mRemoteInputActive;
     private boolean mGlobalActionsVisible = false;
     private boolean mLastGlobalActionsVisible = false;
@@ -278,10 +276,9 @@
             new KeyguardUpdateMonitorCallback() {
         @Override
         public void onEmergencyCallAction() {
-
             // Since we won't get a setOccluded call we have to reset the view manually such that
             // the bouncer goes away.
-            if (mOccluded) {
+            if (mKeyguardStateController.isOccluded()) {
                 reset(true /* hideBouncerWhenShowing */);
             }
         }
@@ -481,7 +478,7 @@
             } else {
                 mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
             }
-        } else if (mShowing && !hideBouncerOverDream) {
+        } else if (mKeyguardStateController.isShowing()  && !hideBouncerOverDream) {
             if (!isWakeAndUnlocking()
                     && !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
                     && !mCentralSurfaces.isInLaunchTransition()
@@ -502,7 +499,7 @@
                     mBouncerInteractor.show(/* isScrimmed= */false);
                 }
             }
-        } else if (!mShowing && isBouncerInTransit()) {
+        } else if (!mKeyguardStateController.isShowing()  && isBouncerInTransit()) {
             // Keyguard is not visible anymore, but expansion animation was still running.
             // We need to hide the bouncer, otherwise it will be stuck in transit.
             if (mBouncer != null) {
@@ -535,9 +532,8 @@
     @Override
     public void show(Bundle options) {
         Trace.beginSection("StatusBarKeyguardViewManager#show");
-        mShowing = true;
         mNotificationShadeWindowController.setKeyguardShowing(true);
-        mKeyguardStateController.notifyKeyguardState(mShowing,
+        mKeyguardStateController.notifyKeyguardState(true,
                 mKeyguardStateController.isOccluded());
         reset(true /* hideBouncerWhenShowing */);
         SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
@@ -601,7 +597,7 @@
         } else {
             mBouncerInteractor.hide();
         }
-        if (mShowing) {
+        if (mKeyguardStateController.isShowing()) {
             // If we were showing the bouncer and then aborting, we need to also clear out any
             // potential actions unless we actually unlocked.
             cancelPostAuthActions();
@@ -618,7 +614,7 @@
     public void showBouncer(boolean scrimmed) {
         resetAlternateAuth(false);
 
-        if (mShowing && !isBouncerShowing()) {
+        if (mKeyguardStateController.isShowing()  && !isBouncerShowing()) {
             if (mBouncer != null) {
                 mBouncer.show(false /* resetSecuritySelection */, scrimmed);
             } else {
@@ -635,7 +631,7 @@
 
     public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
             boolean afterKeyguardGone, String message) {
-        if (mShowing) {
+        if (mKeyguardStateController.isShowing()) {
             try {
                 Trace.beginSection("StatusBarKeyguardViewManager#dismissWithAction");
                 cancelPendingWakeupAction();
@@ -721,11 +717,12 @@
 
     @Override
     public void reset(boolean hideBouncerWhenShowing) {
-        if (mShowing) {
+        if (mKeyguardStateController.isShowing()) {
+            final boolean isOccluded = mKeyguardStateController.isOccluded();
             // Hide quick settings.
-            mNotificationPanelViewController.resetViews(/* animate= */ !mOccluded);
+            mNotificationPanelViewController.resetViews(/* animate= */ !isOccluded);
             // Hide bouncer and quick-quick settings.
-            if (mOccluded && !mDozing) {
+            if (isOccluded && !mDozing) {
                 mCentralSurfaces.hideKeyguard();
                 if (hideBouncerWhenShowing || needsFullscreenBouncer()) {
                     hideBouncer(false /* destroyView */);
@@ -807,7 +804,8 @@
     private void setDozing(boolean dozing) {
         if (mDozing != dozing) {
             mDozing = dozing;
-            if (dozing || mBouncer.needsFullscreenBouncer() || mOccluded) {
+            if (dozing || mBouncer.needsFullscreenBouncer()
+                    || mKeyguardStateController.isOccluded()) {
                 reset(dozing /* hideBouncerWhenShowing */);
             }
             updateStates();
@@ -840,18 +838,23 @@
 
     @Override
     public void setOccluded(boolean occluded, boolean animate) {
-        final boolean isOccluding = !mOccluded && occluded;
-        final boolean isUnOccluding = mOccluded && !occluded;
-        setOccludedAndUpdateStates(occluded);
+        final boolean wasOccluded = mKeyguardStateController.isOccluded();
+        final boolean isOccluding = !wasOccluded && occluded;
+        final boolean isUnOccluding = wasOccluded  && !occluded;
+        mKeyguardStateController.notifyKeyguardState(
+                mKeyguardStateController.isShowing(), occluded);
+        updateStates();
+        final boolean isShowing = mKeyguardStateController.isShowing();
+        final boolean isOccluded = mKeyguardStateController.isOccluded();
 
-        if (mShowing && isOccluding) {
+        if (isShowing && isOccluding) {
             SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
                     SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
             if (mCentralSurfaces.isInLaunchTransition()) {
                 final Runnable endRunnable = new Runnable() {
                     @Override
                     public void run() {
-                        mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
+                        mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
                         reset(true /* hideBouncerWhenShowing */);
                     }
                 };
@@ -866,19 +869,19 @@
                 // When isLaunchingActivityOverLockscreen() is true, we know for sure that the post
                 // collapse runnables will be run.
                 mShadeController.get().addPostCollapseAction(() -> {
-                    mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
+                    mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
                     reset(true /* hideBouncerWhenShowing */);
                 });
                 return;
             }
-        } else if (mShowing && isUnOccluding) {
+        } else if (isShowing && isUnOccluding) {
             SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
                     SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
         }
-        if (mShowing) {
-            mMediaManager.updateMediaMetaData(false, animate && !mOccluded);
+        if (isShowing) {
+            mMediaManager.updateMediaMetaData(false, animate && !isOccluded);
         }
-        mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
+        mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
 
         // setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
         // no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause
@@ -888,20 +891,11 @@
             // by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
             reset(isOccluding /* hideBouncerWhenShowing*/);
         }
-        if (animate && !mOccluded && mShowing && !bouncerIsShowing()) {
+        if (animate && !isOccluded && isShowing && !bouncerIsShowing()) {
             mCentralSurfaces.animateKeyguardUnoccluding();
         }
     }
 
-    private void setOccludedAndUpdateStates(boolean occluded) {
-        mOccluded = occluded;
-        updateStates();
-    }
-
-    public boolean isOccluded() {
-        return mOccluded;
-    }
-
     @Override
     public void startPreHideAnimation(Runnable finishRunnable) {
         if (bouncerIsShowing()) {
@@ -932,8 +926,7 @@
     @Override
     public void hide(long startTime, long fadeoutDuration) {
         Trace.beginSection("StatusBarKeyguardViewManager#hide");
-        mShowing = false;
-        mKeyguardStateController.notifyKeyguardState(mShowing,
+        mKeyguardStateController.notifyKeyguardState(false,
                 mKeyguardStateController.isOccluded());
         launchPendingWakeupAction();
 
@@ -1081,11 +1074,6 @@
                 KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None;
     }
 
-    @Override
-    public boolean isShowing() {
-        return mShowing;
-    }
-
     /**
      * Returns whether a back invocation can be handled, which depends on whether the keyguard
      * is currently showing (which itself is derived from multiple states).
@@ -1184,8 +1172,8 @@
         if (!mCentralSurfacesRegistered) {
             return;
         }
-        boolean showing = mShowing;
-        boolean occluded = mOccluded;
+        boolean showing = mKeyguardStateController.isShowing();
+        boolean occluded = mKeyguardStateController.isOccluded();
         boolean bouncerShowing = bouncerIsShowing();
         boolean bouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing();
         boolean bouncerDismissible = !isFullscreenBouncer();
@@ -1219,13 +1207,6 @@
             mNotificationShadeWindowController.setBouncerShowing(bouncerShowing);
             mCentralSurfaces.setBouncerShowing(bouncerShowing);
         }
-
-        if (occluded != mLastOccluded || mFirstUpdate) {
-            mKeyguardStateController.notifyKeyguardState(showing, occluded);
-        }
-        if (occluded != mLastOccluded || mShowing != showing || mFirstUpdate) {
-            mKeyguardUpdateManager.setKeyguardShowing(showing, occluded);
-        }
         if (bouncerIsOrWillBeShowing != mLastBouncerIsOrWillBeShowing || mFirstUpdate
                 || bouncerShowing != mLastBouncerShowing) {
             mKeyguardUpdateManager.sendKeyguardBouncerChanged(bouncerIsOrWillBeShowing,
@@ -1276,12 +1257,12 @@
     public boolean isNavBarVisible() {
         boolean isWakeAndUnlockPulsing = mBiometricUnlockController != null
                 && mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
-        boolean keyguardShowing = mShowing && !mOccluded;
+        boolean keyguardVisible = mKeyguardStateController.isVisible();
         boolean hideWhileDozing = mDozing && !isWakeAndUnlockPulsing;
-        boolean keyguardWithGestureNav = (keyguardShowing && !mDozing && !mScreenOffAnimationPlaying
+        boolean keyguardWithGestureNav = (keyguardVisible && !mDozing && !mScreenOffAnimationPlaying
                 || mPulsing && !mIsDocked)
                 && mGesturalNav;
-        return (!keyguardShowing && !hideWhileDozing && !mScreenOffAnimationPlaying
+        return (!keyguardVisible && !hideWhileDozing && !mScreenOffAnimationPlaying
                 || bouncerIsShowing()
                 || mRemoteInputActive
                 || keyguardWithGestureNav
@@ -1413,7 +1394,7 @@
         DismissWithActionRequest request = mPendingWakeupAction;
         mPendingWakeupAction = null;
         if (request != null) {
-            if (mShowing) {
+            if (mKeyguardStateController.isShowing()) {
                 dismissWithAction(request.dismissAction, request.cancelAction,
                         request.afterKeyguardGone, request.message);
             } else if (request.dismissAction != null) {
@@ -1432,10 +1413,10 @@
 
     public boolean bouncerNeedsScrimming() {
         // When a dream overlay is active, scrimming will cause any expansion to immediately expand.
-        return (mOccluded && !mDreamOverlayStateController.isOverlayActive())
+        return (mKeyguardStateController.isOccluded()
+                && !mDreamOverlayStateController.isOverlayActive())
                 || bouncerWillDismissWithAction()
-                || (bouncerIsShowing()
-                && bouncerIsScrimmed())
+                || (bouncerIsShowing() && bouncerIsScrimmed())
                 || isFullscreenBouncer();
     }
 
@@ -1454,8 +1435,6 @@
 
     public void dump(PrintWriter pw) {
         pw.println("StatusBarKeyguardViewManager:");
-        pw.println("  mShowing: " + mShowing);
-        pw.println("  mOccluded: " + mOccluded);
         pw.println("  mRemoteInputActive: " + mRemoteInputActive);
         pw.println("  mDozing: " + mDozing);
         pw.println("  mAfterKeyguardGoneAction: " + mAfterKeyguardGoneAction);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
index c96faab..062c3d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
@@ -21,7 +21,9 @@
 /** Provides information about the current wifi network. */
 sealed class WifiNetworkModel {
     /** A model representing that we have no active wifi network. */
-    object Inactive : WifiNetworkModel()
+    object Inactive : WifiNetworkModel() {
+        override fun toString() = "WifiNetwork.Inactive"
+    }
 
     /**
      * A model representing that our wifi network is actually a carrier merged network, meaning it's
@@ -29,7 +31,9 @@
      *
      * See [android.net.wifi.WifiInfo.isCarrierMerged] for more information.
      */
-    object CarrierMerged : WifiNetworkModel()
+    object CarrierMerged : WifiNetworkModel() {
+        override fun toString() = "WifiNetwork.CarrierMerged"
+    }
 
     /** Provides information about an active wifi network. */
     data class Active(
@@ -72,6 +76,24 @@
             }
         }
 
+        override fun toString(): String {
+            // Only include the passpoint-related values in the string if we have them. (Most
+            // networks won't have them so they'll be mostly clutter.)
+            val passpointString =
+                if (isPasspointAccessPoint ||
+                    isOnlineSignUpForPasspointAccessPoint ||
+                    passpointProviderFriendlyName != null) {
+                    ", isPasspointAp=$isPasspointAccessPoint, " +
+                        "isOnlineSignUpForPasspointAp=$isOnlineSignUpForPasspointAccessPoint, " +
+                        "passpointName=$passpointProviderFriendlyName"
+            } else {
+                ""
+            }
+
+            return "WifiNetworkModel.Active(networkId=$networkId, isValidated=$isValidated, " +
+                "level=$level, ssid=$ssid$passpointString)"
+        }
+
         companion object {
             @VisibleForTesting
             internal const val MIN_VALID_LEVEL = 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index 250d9d4..1ae1eae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -35,9 +35,16 @@
     }
 
     /**
-     * If the lock screen is visible.
-     * The keyguard is also visible when the device is asleep or in always on mode, except when
-     * the screen timed out and the user can unlock by quickly pressing power.
+     * If the keyguard is visible. This is unrelated to being locked or not.
+     */
+    default boolean isVisible() {
+        return isShowing() && !isOccluded();
+    }
+
+    /**
+     * If the keyguard is showing. This includes when it's occluded by an activity, and when
+     * the device is asleep or in always on mode, except when the screen timed out and the user
+     * can unlock by quickly pressing power.
      *
      * This is unrelated to being locked or not.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 437d4d4..cc6fdcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -181,6 +181,7 @@
         if (mShowing == showing && mOccluded == occluded) return;
         mShowing = showing;
         mOccluded = occluded;
+        mKeyguardUpdateMonitor.setKeyguardShowing(showing, occluded);
         Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events",
                 "Keyguard showing: " + showing + " occluded: " + occluded);
         notifyKeyguardChanged();
@@ -387,6 +388,8 @@
     @Override
     public void dump(PrintWriter pw, String[] args) {
         pw.println("KeyguardStateController:");
+        pw.println("  mShowing: " + mShowing);
+        pw.println("  mOccluded: " + mOccluded);
         pw.println("  mSecure: " + mSecure);
         pw.println("  mCanDismissLockScreen: " + mCanDismissLockScreen);
         pw.println("  mTrustManaged: " + mTrustManaged);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
new file mode 100644
index 0000000..dd9683f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 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.systemui.assist.ui;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DisplayUtilsTest extends SysuiTestCase {
+
+    @Mock
+    Resources mResources;
+    @Mock
+    Context mMockContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testGetCornerRadii_noOverlay() {
+        assertEquals(0, DisplayUtils.getCornerRadiusBottom(mContext));
+        assertEquals(0, DisplayUtils.getCornerRadiusTop(mContext));
+    }
+
+    @Test
+    public void testGetCornerRadii_onlyDefaultOverridden() {
+        when(mResources.getDimensionPixelSize(R.dimen.config_rounded_mask_size)).thenReturn(100);
+        when(mMockContext.getResources()).thenReturn(mResources);
+
+        assertEquals(100, DisplayUtils.getCornerRadiusBottom(mMockContext));
+        assertEquals(100, DisplayUtils.getCornerRadiusTop(mMockContext));
+    }
+
+    @Test
+    public void testGetCornerRadii_allOverridden() {
+        when(mResources.getDimensionPixelSize(R.dimen.config_rounded_mask_size)).thenReturn(100);
+        when(mResources.getDimensionPixelSize(R.dimen.config_rounded_mask_size_top)).thenReturn(
+                150);
+        when(mResources.getDimensionPixelSize(R.dimen.config_rounded_mask_size_bottom)).thenReturn(
+                200);
+        when(mMockContext.getResources()).thenReturn(mResources);
+
+        assertEquals(200, DisplayUtils.getCornerRadiusBottom(mMockContext));
+        assertEquals(150, DisplayUtils.getCornerRadiusTop(mMockContext));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 11e5880..f210708 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -38,6 +38,7 @@
 import static org.mockito.Mockito.when;
 
 import android.graphics.Rect;
+import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricOverlayConstants;
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.SensorProperties;
@@ -688,7 +689,7 @@
     }
 
     @Test
-    public void aodInterruptCancelTimeoutActionWhenFingerUp() throws RemoteException {
+    public void aodInterruptCancelTimeoutActionOnFingerUp() throws RemoteException {
         when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
 
@@ -740,6 +741,56 @@
     }
 
     @Test
+    public void aodInterruptCancelTimeoutActionOnAcquired() throws RemoteException {
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+
+        // GIVEN AOD interrupt
+        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mScreenObserver.onScreenTurnedOn();
+        mFgExecutor.runAllReady();
+        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
+        mFgExecutor.runAllReady();
+
+        // Configure UdfpsView to accept the acquired event
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+
+        // WHEN acquired is received
+        mOverlayController.onAcquired(TEST_UDFPS_SENSOR_ID,
+                BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD);
+
+        // Configure UdfpsView to accept the ACTION_DOWN event
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+
+        // WHEN ACTION_DOWN is received
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+        mBiometricsExecutor.runAllReady();
+        downEvent.recycle();
+
+        // WHEN ACTION_MOVE is received
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+        mBiometricsExecutor.runAllReady();
+        moveEvent.recycle();
+        mFgExecutor.runAllReady();
+
+        // Configure UdfpsView to accept the finger up event
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+
+        // WHEN it times out
+        mFgExecutor.advanceClockToNext();
+        mFgExecutor.runAllReady();
+
+        // THEN the display should be unconfigured once. If the timeout action is not
+        // cancelled, the display would be unconfigured twice which would cause two
+        // FP attempts.
+        verify(mUdfpsView, times(1)).unconfigureDisplay();
+    }
+
+    @Test
     public void aodInterruptScreenOff() throws RemoteException {
         // GIVEN screen off
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index eec33ca..f370be1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -27,10 +28,10 @@
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.service.dreams.DreamService;
 import android.service.dreams.IDreamOverlay;
 import android.service.dreams.IDreamOverlayCallback;
 import android.testing.AndroidTestingRunner;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
@@ -53,6 +54,8 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -61,6 +64,7 @@
 public class DreamOverlayServiceTest extends SysuiTestCase {
     private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package",
             "lowlight");
+    private static final String DREAM_COMPONENT = "package/dream";
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
 
@@ -108,12 +112,14 @@
     @Mock
     UiEventLogger mUiEventLogger;
 
+    @Captor
+    ArgumentCaptor<View> mViewCaptor;
+
     DreamOverlayService mService;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mContext.addMockSystemService(WindowManager.class, mWindowManager);
 
         when(mDreamOverlayComponent.getDreamOverlayContainerViewController())
                 .thenReturn(mDreamOverlayContainerViewController);
@@ -129,7 +135,7 @@
         when(mDreamOverlayContainerViewController.getContainerView())
                 .thenReturn(mDreamOverlayContainerView);
 
-        mService = new DreamOverlayService(mContext, mMainExecutor,
+        mService = new DreamOverlayService(mContext, mMainExecutor, mWindowManager,
                 mDreamOverlayComponentFactory,
                 mStateController,
                 mKeyguardUpdateMonitor,
@@ -143,7 +149,8 @@
         final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback);
+        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+                false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
         verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
@@ -157,7 +164,8 @@
         final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback);
+        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+                false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
         verify(mWindowManager).addView(any(), any());
@@ -169,7 +177,8 @@
         final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback);
+        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+                false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
         verify(mDreamOverlayContainerViewController).init();
@@ -186,49 +195,76 @@
         final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback);
+        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+                false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
         verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView);
     }
 
     @Test
-    public void testShouldShowComplicationsFalseByDefault() {
-        mService.onBind(new Intent());
+    public void testShouldShowComplicationsSetByStartDream() throws RemoteException {
+        final IBinder proxy = mService.onBind(new Intent());
+        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
 
-        assertThat(mService.shouldShowComplications()).isFalse();
-    }
-
-    @Test
-    public void testShouldShowComplicationsSetByIntentExtra() {
-        final Intent intent = new Intent();
-        intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, true);
-        mService.onBind(intent);
+        // Inform the overlay service of dream starting.
+        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+                true /*shouldShowComplication*/);
 
         assertThat(mService.shouldShowComplications()).isTrue();
     }
 
     @Test
-    public void testLowLightSetByIntentExtra() throws RemoteException {
-        final Intent intent = new Intent();
-        intent.putExtra(DreamService.EXTRA_DREAM_COMPONENT, LOW_LIGHT_COMPONENT);
-
-        final IBinder proxy = mService.onBind(intent);
+    public void testLowLightSetByStartDream() throws RemoteException {
+        final IBinder proxy = mService.onBind(new Intent());
         final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
-        assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT);
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback);
+        overlay.startDream(mWindowParams, mDreamOverlayCallback,
+                LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
+        assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT);
         verify(mStateController).setLowLightActive(true);
     }
 
     @Test
-    public void testDestroy() {
+    public void testDestroy() throws RemoteException {
+        final IBinder proxy = mService.onBind(new Intent());
+        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+        // Inform the overlay service of dream starting.
+        overlay.startDream(mWindowParams, mDreamOverlayCallback,
+                LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
+        mMainExecutor.runAllReady();
+
+        // Verify view added.
+        verify(mWindowManager).addView(mViewCaptor.capture(), any());
+
+        // Service destroyed.
         mService.onDestroy();
         mMainExecutor.runAllReady();
 
+        // Verify view removed.
+        verify(mWindowManager).removeView(mViewCaptor.getValue());
+
+        // Verify state correctly set.
+        verify(mKeyguardUpdateMonitor).removeCallback(any());
+        verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
+        verify(mStateController).setOverlayActive(false);
+        verify(mStateController).setLowLightActive(false);
+    }
+
+    @Test
+    public void testDoNotRemoveViewOnDestroyIfOverlayNotStarted() {
+        // Service destroyed without ever starting dream.
+        mService.onDestroy();
+        mMainExecutor.runAllReady();
+
+        // Verify no view is removed.
+        verify(mWindowManager, never()).removeView(any());
+
+        // Verify state still correctly set.
         verify(mKeyguardUpdateMonitor).removeCallback(any());
         verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
         verify(mStateController).setOverlayActive(false);
@@ -245,7 +281,8 @@
         final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback);
+        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+                false /*shouldShowComplication*/);
 
         // Destroy the service.
         mService.onDestroy();
@@ -255,4 +292,44 @@
 
         verify(mWindowManager, never()).addView(any(), any());
     }
+
+    @Test
+    public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException {
+        final IBinder proxy = mService.onBind(new Intent());
+        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+        // Inform the overlay service of dream starting. Do not show dream complications.
+        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+                false /*shouldShowComplication*/);
+        mMainExecutor.runAllReady();
+
+        // Verify that a new window is added.
+        verify(mWindowManager).addView(mViewCaptor.capture(), any());
+        final View windowDecorView = mViewCaptor.getValue();
+
+        // Assert that the overlay is not showing complications.
+        assertThat(mService.shouldShowComplications()).isFalse();
+
+        clearInvocations(mDreamOverlayComponent);
+        clearInvocations(mWindowManager);
+
+        // New dream starting with dream complications showing. Note that when a new dream is
+        // binding to the dream overlay service, it receives the same instance of IBinder as the
+        // first one.
+        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+                true /*shouldShowComplication*/);
+        mMainExecutor.runAllReady();
+
+        // Assert that the overlay is showing complications.
+        assertThat(mService.shouldShowComplications()).isTrue();
+
+        // Verify that the old overlay window has been removed, and a new one created.
+        verify(mWindowManager).removeView(windowDecorView);
+        verify(mWindowManager).addView(any(), any());
+
+        // Verify that new instances of overlay container view controller and overlay touch monitor
+        // are created.
+        verify(mDreamOverlayComponent).getDreamOverlayContainerViewController();
+        verify(mDreamOverlayComponent).getDreamOverlayTouchMonitor();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 21c018a..39f3c96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -176,7 +176,7 @@
 
         // and the keyguard goes away
         mViewMediator.setShowingLocked(false);
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
         mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false);
 
         TestableLooper.get(this).processAllMessages();
@@ -201,7 +201,7 @@
 
         // and the keyguard goes away
         mViewMediator.setShowingLocked(false);
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
         mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false);
 
         TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index 8073103..6c03730 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -39,7 +39,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -49,6 +48,7 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -113,7 +113,7 @@
         mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
                 mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
                 mSystemActions, mOverviewProxyService, mAssistManagerLazy,
-                () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardViewController.class),
+                () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
                 mNavigationModeController, mUserTracker, mDumpManager);
 
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 51f0953..0e9d279 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -72,7 +72,6 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
-import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestableContext;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
@@ -194,7 +193,7 @@
     @Mock
     private CentralSurfaces mCentralSurfaces;
     @Mock
-    private KeyguardViewController mKeyguardViewController;
+    private KeyguardStateController mKeyguardStateController;
     @Mock
     private UserContextProvider mUserContextProvider;
     @Mock
@@ -240,7 +239,7 @@
                     mock(AccessibilityButtonTargetsObserver.class),
                     mSystemActions, mOverviewProxyService,
                     () -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
-                    mKeyguardViewController, mock(NavigationModeController.class),
+                    mKeyguardStateController, mock(NavigationModeController.class),
                     mock(UserTracker.class), mock(DumpManager.class)));
             mNavigationBar = createNavBar(mContext);
             mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
@@ -380,7 +379,7 @@
 
         // Verify navbar didn't alter and showing back icon when the keyguard is showing without
         // requesting IME insets visible.
-        doReturn(true).when(mKeyguardViewController).isShowing();
+        doReturn(true).when(mKeyguardStateController).isShowing();
         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true);
         assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
index b719c7f..a6381d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
@@ -32,7 +32,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Assert;
@@ -58,8 +57,6 @@
         mDynamicPrivacyController = new DynamicPrivacyController(
                 mLockScreenUserManager, mKeyguardStateController,
                 mock(StatusBarStateController.class));
-        mDynamicPrivacyController.setStatusBarKeyguardViewManager(
-                mock(StatusBarKeyguardViewManager.class));
         mDynamicPrivacyController.addListener(mListener);
         // Disable dynamic privacy by default
         allowNotificationsInPublic(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 365e608..4dea6be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -95,8 +95,6 @@
     @Mock
     private AuthController mAuthController;
     @Mock
-    private DozeParameters mDozeParameters;
-    @Mock
     private MetricsLogger mMetricsLogger;
     @Mock
     private NotificationMediaManager mNotificationMediaManager;
@@ -120,7 +118,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         TestableResources res = getContext().getOrCreateTestableResources();
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
         when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
         when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
@@ -132,7 +130,7 @@
         mBiometricUnlockController = new BiometricUnlockController(mDozeScrimController,
                 mKeyguardViewMediator, mScrimController,
                 mNotificationShadeWindowController, mKeyguardStateController, mHandler,
-                mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
+                mUpdateMonitor, res.getResources(), mKeyguardBypassController,
                 mMetricsLogger, mDumpManager, mPowerManager,
                 mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
                 mAuthController, mStatusBarStateController,
@@ -170,7 +168,7 @@
     public void onBiometricAuthenticated_whenFingerprintAndNotInteractive_wakeAndUnlock() {
         reset(mUpdateMonitor);
         reset(mStatusBarKeyguardViewManager);
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
         when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
         when(mDozeScrimController.isPulsing()).thenReturn(true);
         // the value of isStrongBiometric doesn't matter here since we only care about the returned
@@ -187,7 +185,7 @@
     public void onBiometricAuthenticated_whenDeviceIsAlreadyUnlocked_wakeAndUnlock() {
         reset(mUpdateMonitor);
         reset(mStatusBarKeyguardViewManager);
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
         when(mKeyguardStateController.isUnlocked()).thenReturn(true);
         when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
         when(mDozeScrimController.isPulsing()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 05f8760..ad497a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -516,32 +516,32 @@
 
     @Test
     public void executeRunnableDismissingKeyguard_nullRunnable_showingAndOccluded() {
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
 
         mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
     }
 
     @Test
     public void executeRunnableDismissingKeyguard_nullRunnable_showing() {
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
 
         mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
     }
 
     @Test
     public void executeRunnableDismissingKeyguard_nullRunnable_notShowing() {
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
 
         mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
     }
 
     @Test
     public void executeRunnableDismissingKeyguard_dreaming_notShowing() throws RemoteException {
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         when(mKeyguardUpdateMonitor.isDreaming()).thenReturn(true);
 
         mCentralSurfaces.executeRunnableDismissingKeyguard(() ->  {},
@@ -555,8 +555,8 @@
 
     @Test
     public void executeRunnableDismissingKeyguard_notDreaming_notShowing() throws RemoteException {
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         when(mKeyguardUpdateMonitor.isDreaming()).thenReturn(false);
 
         mCentralSurfaces.executeRunnableDismissingKeyguard(() ->  {},
@@ -571,10 +571,10 @@
     @Test
     public void lockscreenStateMetrics_notShowing() {
         // uninteresting state, except that fingerprint must be non-zero
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
         // interesting state
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
         when(mKeyguardStateController.isMethodSecure()).thenReturn(false);
         mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
@@ -589,10 +589,10 @@
     @Test
     public void lockscreenStateMetrics_notShowing_secure() {
         // uninteresting state, except that fingerprint must be non-zero
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
         // interesting state
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
         when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
 
@@ -608,10 +608,10 @@
     @Test
     public void lockscreenStateMetrics_isShowing() {
         // uninteresting state, except that fingerprint must be non-zero
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
         // interesting state
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
         when(mKeyguardStateController.isMethodSecure()).thenReturn(false);
 
@@ -627,10 +627,10 @@
     @Test
     public void lockscreenStateMetrics_isShowing_secure() {
         // uninteresting state, except that fingerprint must be non-zero
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
         // interesting state
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
         when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
 
@@ -646,10 +646,10 @@
     @Test
     public void lockscreenStateMetrics_isShowingBouncer() {
         // uninteresting state, except that fingerprint must be non-zero
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
         // interesting state
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
         when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
 
@@ -1053,9 +1053,9 @@
     }
 
     @Test
-    public void startActivityDismissingKeyguard_isShowingandIsOccluded() {
-        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
-        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(true);
+    public void startActivityDismissingKeyguard_isShowingAndIsOccluded() {
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
         mCentralSurfaces.startActivityDismissingKeyguard(
                 new Intent(),
                 /* onlyProvisioned = */false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
new file mode 100644
index 0000000..a986777
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 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.systemui.statusbar.phone;
+
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+/**
+ * Mock implementation of KeyguardStateController which tracks showing and occluded states
+ * based on {@link #notifyKeyguardState(boolean showing, boolean occluded)}}.
+ */
+public class FakeKeyguardStateController implements KeyguardStateController {
+    private boolean mShowing;
+    private boolean mOccluded;
+    private boolean mCanDismissLockScreen;
+
+    @Override
+    public void notifyKeyguardState(boolean showing, boolean occluded) {
+        mShowing = showing;
+        mOccluded = occluded;
+    }
+
+    @Override
+    public boolean isShowing() {
+        return mShowing;
+    }
+
+    @Override
+    public boolean isOccluded() {
+        return mOccluded;
+    }
+
+    public void setCanDismissLockScreen(boolean canDismissLockScreen) {
+        mCanDismissLockScreen = canDismissLockScreen;
+    }
+
+    @Override
+    public boolean canDismissLockScreen() {
+        return mCanDismissLockScreen;
+    }
+
+    @Override
+    public boolean isBouncerShowing() {
+        return false;
+    }
+
+    @Override
+    public boolean isKeyguardScreenRotationAllowed() {
+        return false;
+    }
+
+    @Override
+    public boolean isMethodSecure() {
+        return true;
+    }
+
+    @Override
+    public boolean isTrusted() {
+        return false;
+    }
+
+    @Override
+    public boolean isKeyguardGoingAway() {
+        return false;
+    }
+
+    @Override
+    public boolean isKeyguardFadingAway() {
+        return false;
+    }
+
+    @Override
+    public boolean isLaunchTransitionFadingAway() {
+        return false;
+    }
+
+    @Override
+    public long getKeyguardFadingAwayDuration() {
+        return 0;
+    }
+
+    @Override
+    public long getKeyguardFadingAwayDelay() {
+        return 0;
+    }
+
+    @Override
+    public long calculateGoingToFullShadeDelay() {
+        return 0;
+    }
+
+    @Override
+    public float getDismissAmount() {
+        return 0f;
+    }
+
+    @Override
+    public boolean isDismissingFromSwipe() {
+        return false;
+    }
+
+    @Override
+    public boolean isFlingingToDismissKeyguard() {
+        return false;
+    }
+
+    @Override
+    public boolean isFlingingToDismissKeyguardDuringSwipeGesture() {
+        return false;
+    }
+
+    @Override
+    public boolean isSnappingKeyguardBackAfterSwipe() {
+        return false;
+    }
+
+    @Override
+    public void notifyPanelFlingStart(boolean dismiss) {
+    }
+
+    @Override
+    public void notifyPanelFlingEnd() {
+    }
+
+    @Override
+    public void addCallback(Callback listener) {
+    }
+
+    @Override
+    public void removeCallback(Callback listener) {
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index ecbb7e5b..8da8d04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -68,7 +69,6 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 
 import com.google.common.truth.Truth;
@@ -94,7 +94,6 @@
 
     @Mock private ViewMediatorCallback mViewMediatorCallback;
     @Mock private LockPatternUtils mLockPatternUtils;
-    @Mock private KeyguardStateController mKeyguardStateController;
     @Mock private CentralSurfaces mCentralSurfaces;
     @Mock private ViewGroup mContainer;
     @Mock private NotificationPanelViewController mNotificationPanelView;
@@ -123,6 +122,8 @@
 
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
+    private FakeKeyguardStateController mKeyguardStateController =
+            spy(new FakeKeyguardStateController());
 
     @Mock private ViewRootImpl mViewRootImpl;
     @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
@@ -180,7 +181,6 @@
                 mBiometricUnlockController,
                 mNotificationContainer,
                 mBypassController);
-        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         mStatusBarKeyguardViewManager.show(null);
         ArgumentCaptor<KeyguardBouncer.BouncerExpansionCallback> callbackArgumentCaptor =
                 ArgumentCaptor.forClass(KeyguardBouncer.BouncerExpansionCallback.class);
@@ -253,7 +253,7 @@
 
     @Test
     public void onPanelExpansionChanged_showsBouncerWhenSwiping() {
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+        mKeyguardStateController.setCanDismissLockScreen(false);
         mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
         verify(mBouncer).show(eq(false), eq(false));
 
@@ -340,13 +340,12 @@
     }
 
     @Test
-    public void setOccluded_onKeyguardOccludedChangedCalledCorrectly() {
+    public void setOccluded_onKeyguardOccludedChangedCalled() {
         clearInvocations(mKeyguardStateController);
         clearInvocations(mKeyguardUpdateMonitor);
 
-        // Should be false to start, so no invocations
         mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, false /* animated */);
-        verify(mKeyguardStateController, never()).notifyKeyguardState(anyBoolean(), anyBoolean());
+        verify(mKeyguardStateController).notifyKeyguardState(true, false);
 
         clearInvocations(mKeyguardUpdateMonitor);
         clearInvocations(mKeyguardStateController);
@@ -357,8 +356,8 @@
         clearInvocations(mKeyguardUpdateMonitor);
         clearInvocations(mKeyguardStateController);
 
-        mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
-        verify(mKeyguardStateController, never()).notifyKeyguardState(anyBoolean(), anyBoolean());
+        mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, false /* animated */);
+        verify(mKeyguardStateController).notifyKeyguardState(true, false);
     }
 
     @Test
@@ -426,7 +425,7 @@
         when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
         assertTrue(
                 "Is showing not accurate when alternative auth showing",
-                mStatusBarKeyguardViewManager.isShowing());
+                mStatusBarKeyguardViewManager.isBouncerShowing());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index 929e529..a3ad028 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -23,9 +23,9 @@
 import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.statusbar.connectivity.WifiIcons
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
+import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
@@ -144,7 +144,12 @@
 
         /** A function that, given a context, calculates the correct content description string. */
         val contentDescription: (Context) -> String,
-    )
+
+        /** A human-readable description used for the test names. */
+        val description: String,
+    ) {
+        override fun toString() = description
+    }
 
     // Note: We use default values for the boolean parameters to reflect a "typical configuration"
     //   for wifi. This allows each TestCase to only define the parameter values that are critical
@@ -158,12 +163,21 @@
 
         /** The expected output. Null if we expect the output to be null. */
         val expected: Expected?
-    )
+    ) {
+        override fun toString(): String {
+            return "when INPUT(enabled=$enabled, " +
+                "forceHidden=$forceHidden, " +
+                "showWhenEnabled=$alwaysShowIconWhenEnabled, " +
+                "hasDataCaps=$hasDataCapabilities, " +
+                "network=$network) then " +
+                "EXPECTED($expected)"
+        }
+    }
 
     companion object {
-        @Parameters(name = "{0}")
-        @JvmStatic
-        fun data(): Collection<TestCase> =
+        @Parameters(name = "{0}") @JvmStatic fun data(): Collection<TestCase> = testData
+
+        private val testData: List<TestCase> =
             listOf(
                 // Enabled = false => no networks shown
                 TestCase(
@@ -215,11 +229,12 @@
                     network = WifiNetworkModel.Inactive,
                     expected =
                         Expected(
-                            iconResource = WifiIcons.WIFI_NO_NETWORK,
+                            iconResource = WIFI_NO_NETWORK,
                             contentDescription = { context ->
                                 "${context.getString(WIFI_NO_CONNECTION)}," +
                                     context.getString(NO_INTERNET)
-                            }
+                            },
+                            description = "No network icon",
                         ),
                 ),
                 TestCase(
@@ -231,7 +246,8 @@
                             contentDescription = { context ->
                                 "${context.getString(WIFI_CONNECTION_STRENGTH[4])}," +
                                     context.getString(NO_INTERNET)
-                            }
+                            },
+                            description = "No internet level 4 icon",
                         ),
                 ),
                 TestCase(
@@ -242,7 +258,8 @@
                             iconResource = WIFI_FULL_ICONS[2],
                             contentDescription = { context ->
                                 context.getString(WIFI_CONNECTION_STRENGTH[2])
-                            }
+                            },
+                            description = "Full internet level 2 icon",
                         ),
                 ),
 
@@ -252,11 +269,12 @@
                     network = WifiNetworkModel.Inactive,
                     expected =
                         Expected(
-                            iconResource = WifiIcons.WIFI_NO_NETWORK,
+                            iconResource = WIFI_NO_NETWORK,
                             contentDescription = { context ->
                                 "${context.getString(WIFI_NO_CONNECTION)}," +
                                     context.getString(NO_INTERNET)
-                            }
+                            },
+                            description = "No network icon",
                         ),
                 ),
                 TestCase(
@@ -268,7 +286,8 @@
                             contentDescription = { context ->
                                 "${context.getString(WIFI_CONNECTION_STRENGTH[2])}," +
                                     context.getString(NO_INTERNET)
-                            }
+                            },
+                            description = "No internet level 2 icon",
                         ),
                 ),
                 TestCase(
@@ -279,7 +298,8 @@
                             iconResource = WIFI_FULL_ICONS[0],
                             contentDescription = { context ->
                                 context.getString(WIFI_CONNECTION_STRENGTH[0])
-                            }
+                            },
+                            description = "Full internet level 0 icon",
                         ),
                 ),
 
@@ -309,7 +329,8 @@
                             iconResource = WIFI_FULL_ICONS[4],
                             contentDescription = { context ->
                                 context.getString(WIFI_CONNECTION_STRENGTH[4])
-                            }
+                            },
+                            description = "Full internet level 4 icon",
                         ),
                 ),
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
index 15ba672..4ca1fd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
@@ -26,6 +26,7 @@
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
 import org.junit.Assert.assertTrue
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -35,6 +36,7 @@
 
     private val serializer = IpcSerializer()
 
+    @Ignore("b/253046405")
     @Test
     fun serializeManyIncomingIpcs(): Unit = runBlocking(Dispatchers.Main.immediate) {
         val processor = launch(start = CoroutineStart.LAZY) { serializer.process() }
diff --git a/services/Android.bp b/services/Android.bp
index 637c4ee..76a1484 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -88,7 +88,6 @@
         ":services.appwidget-sources",
         ":services.autofill-sources",
         ":services.backup-sources",
-        ":backuplib-sources",
         ":services.companion-sources",
         ":services.contentcapture-sources",
         ":services.contentsuggestions-sources",
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index ead8aff..b086406 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -19,5 +19,5 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.backup-sources"],
     libs: ["services.core"],
-    static_libs: ["backuplib", "app-compat-annotations"],
+    static_libs: ["app-compat-annotations"],
 }
diff --git a/services/backup/backuplib/Android.bp b/services/backup/backuplib/Android.bp
deleted file mode 100644
index 5a28891..0000000
--- a/services/backup/backuplib/Android.bp
+++ /dev/null
@@ -1,21 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-filegroup {
-    name: "backuplib-sources",
-    srcs: ["java/**/*.java"],
-    path: "java",
-    visibility: ["//frameworks/base/services"],
-}
-
-java_library {
-    name: "backuplib",
-    srcs: [":backuplib-sources"],
-    libs: ["services.core"],
-}
diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
similarity index 100%
rename from services/backup/backuplib/java/com/android/server/backup/TransportManager.java
rename to services/backup/java/com/android/server/backup/TransportManager.java
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
similarity index 99%
rename from services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
rename to services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
index d75d648..237a3fa 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -376,7 +376,7 @@
         try {
             return future.get(600, TimeUnit.SECONDS);
         } catch (InterruptedException | ExecutionException | TimeoutException
-                | CancellationException e) {
+                 | CancellationException e) {
             Slog.w(TAG, "Failed to get result from transport:", e);
             return null;
         } finally {
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/OnTransportRegisteredListener.java b/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
similarity index 100%
rename from services/backup/backuplib/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
rename to services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java b/services/backup/java/com/android/server/backup/transport/TransportConnection.java
similarity index 100%
rename from services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java
rename to services/backup/java/com/android/server/backup/transport/TransportConnection.java
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java b/services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java
similarity index 89%
rename from services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java
rename to services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java
index 1776c41..b218a29 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -11,15 +11,13 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.backup.transport;
 
 import android.annotation.Nullable;
 
-import com.android.server.backup.transport.BackupTransportClient;
-
 /**
  * Listener to be called by {@link TransportConnection#connectAsync(TransportConnectionListener,
  * String)}.
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionManager.java b/services/backup/java/com/android/server/backup/transport/TransportConnectionManager.java
similarity index 100%
rename from services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionManager.java
rename to services/backup/java/com/android/server/backup/transport/TransportConnectionManager.java
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java b/services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java
similarity index 100%
rename from services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java
rename to services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotRegisteredException.java b/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java
similarity index 100%
rename from services/backup/backuplib/java/com/android/server/backup/transport/TransportNotRegisteredException.java
rename to services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java b/services/backup/java/com/android/server/backup/transport/TransportStats.java
similarity index 100%
rename from services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java
rename to services/backup/java/com/android/server/backup/transport/TransportStats.java
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java b/services/backup/java/com/android/server/backup/transport/TransportStatusCallback.java
similarity index 97%
rename from services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
rename to services/backup/java/com/android/server/backup/transport/TransportStatusCallback.java
index 99526b7..fb98825 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportStatusCallback.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
similarity index 100%
rename from services/backup/backuplib/java/com/android/server/backup/transport/TransportUtils.java
rename to services/backup/java/com/android/server/backup/transport/TransportUtils.java
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 4018be1..0f101b0 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -42,6 +42,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
+import android.app.assist.ActivityId;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
 import android.content.ContentResolver;
@@ -916,13 +917,16 @@
             return mGlobalContentCaptureOptions.getOptions(userId, packageName);
         }
 
+        // ErrorProne says ContentCaptureManagerService.this.mLock needs to be guarded by
+        // 'service.mLock', which is the same as mLock.
+        @SuppressWarnings("GuardedBy")
         @Override
         public void notifyActivityEvent(int userId, @NonNull ComponentName activityComponent,
-                @ActivityEventType int eventType) {
+                @ActivityEventType int eventType, @NonNull ActivityId activityId) {
             synchronized (mLock) {
                 final ContentCapturePerUserService service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    service.onActivityEventLocked(activityComponent, eventType);
+                    service.onActivityEventLocked(activityId, activityComponent, eventType);
                 }
             }
         }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index a08687f..cee78e0 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -548,12 +548,13 @@
     }
 
     @GuardedBy("mLock")
-    void onActivityEventLocked(@NonNull ComponentName componentName, @ActivityEventType int type) {
+    void onActivityEventLocked(@NonNull ActivityId activityId,
+            @NonNull ComponentName componentName, @ActivityEventType int type) {
         if (mRemoteService == null) {
             if (mMaster.debug) Slog.d(mTag, "onActivityEvent(): no remote service");
             return;
         }
-        final ActivityEvent event = new ActivityEvent(componentName, type);
+        final ActivityEvent event = new ActivityEvent(activityId, componentName, type);
 
         if (mMaster.verbose) Slog.v(mTag, "onActivityEvent(): " + event);
 
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 285c406..41044bf 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -16,12 +16,18 @@
 
 package android.os;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
 import com.android.internal.os.BinderCallsStats;
 import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Collection;
 import java.util.List;
 
+
 /**
  * Battery stats local system service interface. This is used to pass internal data out of
  * BatteryStatsImpl, as well as make unchecked calls into BatteryStatsImpl.
@@ -29,6 +35,19 @@
  * @hide Only for use within Android OS.
  */
 public abstract class BatteryStatsInternal {
+
+    public static final int CPU_WAKEUP_SUBSYSTEM_UNKNOWN = -1;
+    public static final int CPU_WAKEUP_SUBSYSTEM_ALARM = 1;
+
+    /** @hide */
+    @IntDef(prefix = {"CPU_WAKEUP_SUBSYSTEM_"}, value = {
+            CPU_WAKEUP_SUBSYSTEM_UNKNOWN,
+            CPU_WAKEUP_SUBSYSTEM_ALARM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface CpuWakeupSubsystem {
+    }
+
     /**
      * Returns the wifi interfaces.
      */
@@ -56,9 +75,10 @@
     /**
      * Inform battery stats how many deferred jobs existed when the app got launched and how
      * long ago was the last job execution for the app.
-     * @param uid the uid of the app.
+     *
+     * @param uid         the uid of the app.
      * @param numDeferred number of deferred jobs.
-     * @param sinceLast how long in millis has it been since a job was run
+     * @param sinceLast   how long in millis has it been since a job was run
      */
     public abstract void noteJobsDeferred(int uid, int numDeferred, long sinceLast);
 
@@ -72,4 +92,11 @@
      * Informs battery stats of native thread IDs of threads taking incoming binder calls.
      */
     public abstract void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
+
+    /**
+     * Reports any activity that could potentially have caused the CPU to wake up.
+     * Accepts a timestamp to allow the reporter to report it before or after the event.
+     */
+    public abstract void noteCpuWakingActivity(@CpuWakeupSubsystem int subsystem,
+            long elapsedMillis, @NonNull int... uids);
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ee13118..c837051 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -204,6 +204,7 @@
 import android.app.ProfilerInfo;
 import android.app.SyncNotedAppOp;
 import android.app.WaitResult;
+import android.app.assist.ActivityId;
 import android.app.backup.BackupManager.OperationType;
 import android.app.backup.IBackupManager;
 import android.app.compat.CompatChanges;
@@ -2915,7 +2916,7 @@
      * @param taskRoot Task's root
      */
     public void updateActivityUsageStats(ComponentName activity, int userId, int event,
-            IBinder appToken, ComponentName taskRoot) {
+            IBinder appToken, ComponentName taskRoot, ActivityId activityId) {
         if (DEBUG_SWITCH) {
             Slog.d(TAG_SWITCH, "updateActivityUsageStats: comp="
                     + activity + " hash=" + appToken.hashCode() + " event=" + event);
@@ -2932,7 +2933,7 @@
         if (contentCaptureService != null && (event == Event.ACTIVITY_PAUSED
                 || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED
                 || event == Event.ACTIVITY_DESTROYED)) {
-            contentCaptureService.notifyActivityEvent(userId, activity, event);
+            contentCaptureService.notifyActivityEvent(userId, activity, event, activityId);
         }
         // Currently we have move most of logic to the client side. When the activity lifecycle
         // event changed, the client side will notify the VoiceInteractionManagerService. But
@@ -17106,9 +17107,9 @@
 
         @Override
         public void updateActivityUsageStats(ComponentName activity, int userId, int event,
-                IBinder appToken, ComponentName taskRoot) {
+                IBinder appToken, ComponentName taskRoot, ActivityId activityId) {
             ActivityManagerService.this.updateActivityUsageStats(activity, userId, event,
-                    appToken, taskRoot);
+                    appToken, taskRoot, activityId);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d9d29d65..75d1f68 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -81,9 +81,11 @@
 import android.telephony.ModemActivityInfo;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.util.StatsEvent;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BackgroundThread;
@@ -104,6 +106,7 @@
 import com.android.server.power.stats.BatteryStatsImpl;
 import com.android.server.power.stats.BatteryUsageStatsProvider;
 import com.android.server.power.stats.BatteryUsageStatsStore;
+import com.android.server.power.stats.CpuWakeupStats;
 import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
 
 import java.io.File;
@@ -120,6 +123,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
@@ -142,6 +146,8 @@
 
     private final PowerProfile mPowerProfile;
     final BatteryStatsImpl mStats;
+    @GuardedBy("mWakeupStats")
+    final CpuWakeupStats mCpuWakeupStats;
     private final BatteryUsageStatsStore mBatteryUsageStatsStore;
     private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
     private final Context mContext;
@@ -373,6 +379,7 @@
         }
         mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats,
                 mBatteryUsageStatsStore);
+        mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map);
     }
 
     public void publish() {
@@ -464,6 +471,12 @@
                 mStats.noteBinderThreadNativeIds(binderThreadNativeTids);
             }
         }
+
+        @Override
+        public void noteCpuWakingActivity(int subsystem, long elapsedMillis, int... uids) {
+            Objects.requireNonNull(uids);
+            mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids);
+        }
     }
 
     @Override
@@ -2251,12 +2264,13 @@
             try {
                 String reason;
                 while ((reason = waitWakeup()) != null) {
+                    final long nowElapsed = SystemClock.elapsedRealtime();
+                    final long nowUptime = SystemClock.uptimeMillis();
                     // Wait for the completion of pending works if there is any
                     awaitCompletion();
-
+                    mCpuWakeupStats.noteWakeupTimeAndReason(nowElapsed, nowUptime, reason);
                     synchronized (mStats) {
-                        mStats.noteWakeupReasonLocked(reason,
-                                SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
+                        mStats.noteWakeupReasonLocked(reason, nowElapsed, nowUptime);
                     }
                 }
             } catch (RuntimeException e) {
@@ -2312,6 +2326,7 @@
         pw.println("  --read-daily: read-load last written daily stats.");
         pw.println("  --settings: dump the settings key/values related to batterystats");
         pw.println("  --cpu: dump cpu stats for debugging purpose");
+        pw.println("  --wakeups: dump CPU wakeup history and attribution.");
         pw.println("  --power-profile: dump the power profile constants");
         pw.println("  --usage: write battery usage stats. Optional arguments:");
         pw.println("     --proto: output as a binary protobuffer");
@@ -2567,6 +2582,10 @@
                     }
                     dumpUsageStatsToProto(fd, pw, model, proto);
                     return;
+                } else if ("--wakeups".equals(arg)) {
+                    mCpuWakeupStats.dump(new IndentingPrintWriter(pw, "  "),
+                            SystemClock.elapsedRealtime());
+                    return;
                 } else if ("-a".equals(arg)) {
                     flags |= BatteryStats.DUMP_VERBOSE;
                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
@@ -2697,12 +2716,16 @@
         } else {
             if (DBG) Slog.d(TAG, "begin dumpLocked from UID " + Binder.getCallingUid());
             awaitCompletion();
+
             synchronized (mStats) {
                 mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
                 if (writeData) {
                     mStats.writeAsyncLocked();
                 }
             }
+            pw.println();
+            mCpuWakeupStats.dump(new IndentingPrintWriter(pw, "  "), SystemClock.elapsedRealtime());
+
             if (DBG) Slog.d(TAG, "end dumpLocked");
         }
     }
diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
index c33b5f1..0812fd9 100644
--- a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
+++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.assist.ActivityId;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
 import android.os.Bundle;
@@ -61,5 +62,6 @@
      * Notifies the intelligence service of a high-level activity event for the given user.
      */
     public abstract void notifyActivityEvent(@UserIdInt int userId,
-            @NonNull ComponentName activityComponent, @ActivityEventType int eventType);
+            @NonNull ComponentName activityComponent, @ActivityEventType int eventType,
+            @NonNull ActivityId activityId);
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2be84d0..8481560 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -105,7 +105,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
@@ -190,7 +189,7 @@
 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
 import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
 import com.android.server.pm.UserManagerInternal;
-import com.android.server.statusbar.StatusBarManagerService;
+import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.utils.PriorityDump;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -339,7 +338,7 @@
     // Ongoing notification
     private NotificationManager mNotificationManager;
     KeyguardManager mKeyguardManager;
-    private @Nullable StatusBarManagerService mStatusBar;
+    @Nullable private StatusBarManagerInternal mStatusBarManagerInternal;
     private final Notification.Builder mImeSwitcherNotification;
     private final PendingIntent mImeSwitchPendingIntent;
     private boolean mShowOngoingImeSwitcherForPhones;
@@ -1650,9 +1649,7 @@
             // Called on ActivityManager thread.
             // TODO: Dispatch this to a worker thread as needed.
             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
-                StatusBarManagerService statusBarService = (StatusBarManagerService) ServiceManager
-                        .getService(Context.STATUS_BAR_SERVICE);
-                mService.systemRunning(statusBarService);
+                mService.systemRunning();
             }
         }
 
@@ -1933,7 +1930,7 @@
     /**
      * TODO(b/32343335): The entire systemRunning() method needs to be revisited.
      */
-    public void systemRunning(StatusBarManagerService statusBar) {
+    public void systemRunning() {
         synchronized (ImfLock.class) {
             if (DEBUG) {
                 Slog.d(TAG, "--- systemReady");
@@ -1946,7 +1943,8 @@
                         !mUserManagerInternal.isUserUnlockingOrUnlocked(currentUserId));
                 mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
                 mNotificationManager = mContext.getSystemService(NotificationManager.class);
-                mStatusBar = statusBar;
+                mStatusBarManagerInternal =
+                        LocalServices.getService(StatusBarManagerInternal.class);
                 hideStatusBarIconLocked();
                 updateSystemUiLocked(mImeWindowVis, mBackDisposition);
                 mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
@@ -2965,11 +2963,11 @@
                     final CharSequence contentDescription = applicationInfo != null
                             ? userAwarePackageManager.getApplicationLabel(applicationInfo)
                             : null;
-                    if (mStatusBar != null) {
-                        mStatusBar.setIcon(mSlotIme, packageName, iconId, 0,
+                    if (mStatusBarManagerInternal != null) {
+                        mStatusBarManagerInternal.setIcon(mSlotIme, packageName, iconId, 0,
                                 contentDescription  != null
                                         ? contentDescription.toString() : null);
-                        mStatusBar.setIconVisibility(mSlotIme, true);
+                        mStatusBarManagerInternal.setIconVisibility(mSlotIme, true);
                     }
                 }
             } finally {
@@ -2980,8 +2978,8 @@
 
     @GuardedBy("ImfLock.class")
     private void hideStatusBarIconLocked() {
-        if (mStatusBar != null) {
-            mStatusBar.setIconVisibility(mSlotIme, false);
+        if (mStatusBarManagerInternal != null) {
+            mStatusBarManagerInternal.setIconVisibility(mSlotIme, false);
         }
     }
 
@@ -3160,9 +3158,9 @@
             }
             // mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
             final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
-            if (mStatusBar != null) {
-                mStatusBar.setImeWindowStatus(mCurTokenDisplayId, getCurTokenLocked(), vis,
-                        backDisposition, needsToShowImeSwitcher);
+            if (mStatusBarManagerInternal != null) {
+                mStatusBarManagerInternal.setImeWindowStatus(mCurTokenDisplayId,
+                        getCurTokenLocked(), vis, backDisposition, needsToShowImeSwitcher);
             }
             final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
             if (imi != null && needsToShowImeSwitcher) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4b18add..77fea09 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4872,6 +4872,13 @@
         }
 
         @Override
+        public int getHintsFromListenerNoToken() {
+            synchronized (mNotificationLock) {
+                return mListenerHints;
+            }
+        }
+
+        @Override
         public void requestInterruptionFilterFromListener(INotificationListener token,
                 int interruptionFilter) throws RemoteException {
             final long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 15cd639..423c2760 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -91,7 +91,7 @@
  * other hand, not overriding in {@link ComputerLocked} may leave a function walking
  * unstable data.
  */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public interface Computer extends PackageDataSnapshot {
 
     int getVersion();
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index ed846db..bef87c4 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -30,7 +30,6 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.MATCH_APEX;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -49,7 +48,6 @@
 
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
-import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT;
 import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_INFO;
@@ -116,7 +114,6 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
@@ -145,7 +142,6 @@
 import com.android.server.pm.pkg.component.ParsedService;
 import com.android.server.pm.resolution.ComponentResolverApi;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.pm.verify.domain.DomainVerificationUtils;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
@@ -411,6 +407,7 @@
     private final CompilerStats mCompilerStats;
     private final BackgroundDexOptService mBackgroundDexOptService;
     private final PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy;
+    private final CrossProfileIntentResolverEngine mCrossProfileIntentResolverEngine;
 
     // PackageManagerService attributes that are primitives are referenced through the
     // pms object directly.  Primitives are the only attributes so referenced.
@@ -464,6 +461,8 @@
         mCompilerStats = args.service.mCompilerStats;
         mBackgroundDexOptService = args.service.mBackgroundDexOptService;
         mExternalSourcesPolicy = args.service.mExternalSourcesPolicy;
+        mCrossProfileIntentResolverEngine = new CrossProfileIntentResolverEngine(
+                mUserManager, mDomainVerificationManager, mDefaultAppProvider);
 
         // Used to reference PMS attributes that are primitives and which are not
         // updated under control of the PMS lock.
@@ -735,101 +734,72 @@
         // reader
         boolean sortResult = false;
         boolean addInstant = false;
-        List<ResolveInfo> result = null;
+        List<ResolveInfo> result = new ArrayList<>();
+        // crossProfileResults will hold resolve infos from resolution across profiles.
+        List<CrossProfileDomainInfo> crossProfileResults = new ArrayList<>();
         if (pkgName == null) {
-            List<CrossProfileIntentFilter> matchingFilters =
-                    getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
-            // Check for results that need to skip the current profile.
-            ResolveInfo skipProfileInfo  = querySkipCurrentProfileIntents(matchingFilters,
-                    intent, resolvedType, flags, userId);
-            if (skipProfileInfo != null) {
-                List<ResolveInfo> xpResult = new ArrayList<>(1);
-                xpResult.add(skipProfileInfo);
-                return new QueryIntentActivitiesResult(
-                        applyPostResolutionFilter(
-                                filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
-                                allowDynamicSplits, filterCallingUid, resolveForStart, userId,
-                                intent));
+            if (!mCrossProfileIntentResolverEngine.shouldSkipCurrentProfile(this, intent,
+                    resolvedType, userId)) {
+                /*
+                 Check for results in the current profile only if there is no
+                 {@link CrossProfileIntentFilter} for user with flag
+                 {@link PackageManager.SKIP_CURRENT_PROFILE} set.
+                 */
+                result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this,
+                        intent, resolvedType, flags, userId), userId));
             }
-
-            // Check for results in the current profile.
-            result = filterIfNotSystemUser(mComponentResolver.queryActivities(this,
-                    intent, resolvedType, flags, userId), userId);
             addInstant = isInstantAppResolutionAllowed(intent, result, userId,
                     false /*skipPackageCheck*/, flags);
-            // Check for cross profile results.
+
             boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
-            CrossProfileDomainInfo specificXpInfo = queryCrossProfileIntents(
-                    matchingFilters, intent, resolvedType, flags, userId,
-                    hasNonNegativePriorityResult);
-            if (intent.hasWebURI()) {
-                CrossProfileDomainInfo generalXpInfo = null;
-                final UserInfo parent = getProfileParent(userId);
-                if (parent != null) {
-                    generalXpInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType,
-                            flags, userId, parent.id);
-                }
 
-                // Generalized cross profile intents take precedence over specific.
-                // Note that this is the opposite of the intuitive order.
-                CrossProfileDomainInfo prioritizedXpInfo =
-                        generalXpInfo != null ? generalXpInfo : specificXpInfo;
-
-                if (!addInstant) {
-                    if (result.isEmpty() && prioritizedXpInfo != null) {
-                        // No result in current profile, but found candidate in parent user.
-                        // And we are not going to add ephemeral app, so we can return the
-                        // result straight away.
-                        result.add(prioritizedXpInfo.mResolveInfo);
-                        return new QueryIntentActivitiesResult(
-                                applyPostResolutionFilter(result, instantAppPkgName,
-                                        allowDynamicSplits, filterCallingUid, resolveForStart,
-                                        userId, intent));
-                    } else if (result.size() <= 1 && prioritizedXpInfo == null) {
-                        // No result in parent user and <= 1 result in current profile, and we
-                        // are not going to add ephemeral app, so we can return the result
-                        // without further processing.
-                        return new QueryIntentActivitiesResult(
-                                applyPostResolutionFilter(result, instantAppPkgName,
-                                        allowDynamicSplits, filterCallingUid, resolveForStart,
-                                        userId, intent));
-                    }
-                }
-
-                // We have more than one candidate (combining results from current and parent
-                // profile), so we need filtering and sorting.
-                result = filterCandidatesWithDomainPreferredActivitiesLPr(
-                        intent, flags, result, prioritizedXpInfo, userId);
-                sortResult = true;
-            } else {
-                // If not web Intent, just add result to candidate set and let ResolverActivity
-                // figure it out.
-                if (specificXpInfo != null) {
-                    result.add(specificXpInfo.mResolveInfo);
-                    sortResult = true;
-                }
-            }
+            /*
+             Calling {@link com.android.server.pm.CrossProfileIntentResolverEngine#resolveIntent} to
+             get list of {@link CrossProfileDomainInfo} which have {@link ResolveInfo}s from linked
+             profiles.
+             */
+            crossProfileResults = mCrossProfileIntentResolverEngine.resolveIntent(this, intent,
+                    resolvedType, userId, flags, pkgName, hasNonNegativePriorityResult,
+                    mSettings::getPackage);
+            if (intent.hasWebURI() || !crossProfileResults.isEmpty()) sortResult = true;
         } else {
             final PackageStateInternal setting =
                     getPackageStateInternal(pkgName, Process.SYSTEM_UID);
-            result = null;
+
             if (setting != null && setting.getAndroidPackage() != null && (resolveForStart
                     || !shouldFilterApplication(setting, filterCallingUid, userId))) {
-                result = filterIfNotSystemUser(mComponentResolver.queryActivities(this,
+                result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this,
                         intent, resolvedType, flags, setting.getAndroidPackage().getActivities(),
-                        userId), userId);
+                        userId), userId));
             }
             if (result == null || result.size() == 0) {
                 // the caller wants to resolve for a particular package; however, there
                 // were no installed results, so, try to find an ephemeral result
                 addInstant = isInstantAppResolutionAllowed(intent, null /*result*/, userId,
                         true /*skipPackageCheck*/, flags);
-                if (result == null) {
-                    result = new ArrayList<>();
-                }
             }
+            /*
+             Calling {@link com.android.server.pm.CrossProfileIntentResolverEngine#resolveIntent} to
+             get list of {@link CrossProfileDomainInfo} which have {@link ResolveInfo}s from linked
+             profiles.
+             */
+            crossProfileResults = mCrossProfileIntentResolverEngine.resolveIntent(this, intent,
+                    resolvedType, userId, flags, pkgName, false,
+                    mSettings::getPackage);
         }
-        return new QueryIntentActivitiesResult(sortResult, addInstant, result);
+
+        /*
+             Calling {@link com.android.server.pm.
+             CrossProfileIntentResolverEngine#combineFilterAndCreateQueryAcitivitesResponse} to
+             combine results from current and cross profiles. This also filters any resolve info
+             based on domain preference(if required).
+         */
+        return mCrossProfileIntentResolverEngine
+                .combineFilterAndCreateQueryActivitiesResponse(this, intent, resolvedType,
+                        instantAppPkgName, pkgName, allowDynamicSplits, flags, userId,
+                        filterCallingUid, resolveForStart, result, crossProfileResults,
+                        areWebInstantAppsDisabled(userId), addInstant, sortResult,
+                        mSettings::getPackage);
     }
 
     /**
@@ -1046,126 +1016,6 @@
         return null;
     }
 
-    protected ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
-            Intent intent, long matchFlags, List<ResolveInfo> candidates,
-            CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
-        final ArrayList<ResolveInfo> result = new ArrayList<>();
-        final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
-        final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
-
-        // Blocking instant apps is usually done in applyPostResolutionFilter, but since
-        // domain verification can resolve to a single result, which can be an instant app,
-        // it will then be filtered to an empty list in that method. Instead, do blocking
-        // here so that instant apps can be ignored for approval filtering and a lower
-        // priority result chosen instead.
-        final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
-
-        final int count = candidates.size();
-        // First, try to use approved apps.
-        for (int n = 0; n < count; n++) {
-            ResolveInfo info = candidates.get(n);
-            if (blockInstant && (info.isInstantAppAvailable
-                    || isInstantAppInternal(info.activityInfo.packageName, userId,
-                            Process.SYSTEM_UID))) {
-                continue;
-            }
-
-            // Add to the special match all list (Browser use case)
-            if (info.handleAllWebDataURI) {
-                matchAllList.add(info);
-            } else {
-                undefinedList.add(info);
-            }
-        }
-
-        // We'll want to include browser possibilities in a few cases
-        boolean includeBrowser = false;
-
-        if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) {
-            result.addAll(undefinedList);
-            // Maybe add one for the other profile.
-            if (xpDomainInfo != null && xpDomainInfo.mHighestApprovalLevel
-                    > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) {
-                result.add(xpDomainInfo.mResolveInfo);
-            }
-            includeBrowser = true;
-        } else {
-            Pair<List<ResolveInfo>, Integer> infosAndLevel = mDomainVerificationManager
-                    .filterToApprovedApp(intent, undefinedList, userId,
-                            mSettings::getPackage);
-            List<ResolveInfo> approvedInfos = infosAndLevel.first;
-            Integer highestApproval = infosAndLevel.second;
-
-            // If no apps are approved for the domain, resolve only to browsers
-            if (approvedInfos.isEmpty()) {
-                includeBrowser = true;
-                if (xpDomainInfo != null && xpDomainInfo.mHighestApprovalLevel
-                        > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) {
-                    result.add(xpDomainInfo.mResolveInfo);
-                }
-            } else {
-                result.addAll(approvedInfos);
-
-                // If the other profile has an app that's higher approval, add it
-                if (xpDomainInfo != null
-                        && xpDomainInfo.mHighestApprovalLevel > highestApproval) {
-                    result.add(xpDomainInfo.mResolveInfo);
-                }
-            }
-        }
-
-        if (includeBrowser) {
-            // Also add browsers (all of them or only the default one)
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.v(TAG, "   ...including browsers in candidate set");
-            }
-            if ((matchFlags & MATCH_ALL) != 0) {
-                result.addAll(matchAllList);
-            } else {
-                // Browser/generic handling case.  If there's a default browser, go straight
-                // to that (but only if there is no other higher-priority match).
-                final String defaultBrowserPackageName = mDefaultAppProvider.getDefaultBrowser(
-                        userId);
-                int maxMatchPrio = 0;
-                ResolveInfo defaultBrowserMatch = null;
-                final int numCandidates = matchAllList.size();
-                for (int n = 0; n < numCandidates; n++) {
-                    ResolveInfo info = matchAllList.get(n);
-                    // track the highest overall match priority...
-                    if (info.priority > maxMatchPrio) {
-                        maxMatchPrio = info.priority;
-                    }
-                    // ...and the highest-priority default browser match
-                    if (info.activityInfo.packageName.equals(defaultBrowserPackageName)) {
-                        if (defaultBrowserMatch == null
-                                || (defaultBrowserMatch.priority < info.priority)) {
-                            if (debug) {
-                                Slog.v(TAG, "Considering default browser match " + info);
-                            }
-                            defaultBrowserMatch = info;
-                        }
-                    }
-                }
-                if (defaultBrowserMatch != null
-                        && defaultBrowserMatch.priority >= maxMatchPrio
-                        && !TextUtils.isEmpty(defaultBrowserPackageName)) {
-                    if (debug) {
-                        Slog.v(TAG, "Default browser match " + defaultBrowserMatch);
-                    }
-                    result.add(defaultBrowserMatch);
-                } else {
-                    result.addAll(matchAllList);
-                }
-            }
-
-            // If there is nothing selected, add all candidates
-            if (result.size() == 0) {
-                result.addAll(candidates);
-            }
-        }
-        return result;
-    }
-
     /**
      * Report the 'Home' activity which is currently set as "always use this one". If non is set
      * then reports the most likely home activity or null if there are more than one.
@@ -1279,7 +1129,8 @@
 
             if (result == null) {
                 result = new CrossProfileDomainInfo(createForwardingResolveInfoUnchecked(
-                        new WatchedIntentFilter(), sourceUserId, parentUserId), approvalLevel);
+                        new WatchedIntentFilter(), sourceUserId, parentUserId), approvalLevel,
+                        parentUserId);
             } else {
                 result.mHighestApprovalLevel =
                         Math.max(approvalLevel, result.mHighestApprovalLevel);
@@ -1303,7 +1154,8 @@
             Intent intent, String resolvedType, int userId) {
         CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
         if (resolver != null) {
-            return resolver.queryIntent(this, intent, resolvedType, false /*defaultOnly*/, userId);
+            return resolver.queryIntent(this, intent, resolvedType, false /*defaultOnly*/,
+                    userId);
         }
         return null;
     }
@@ -1467,30 +1319,6 @@
         return resolveInfos;
     }
 
-    private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
-            long matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
-            int userId) {
-        final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
-
-        if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
-            Slog.v(TAG, "Filtering results with preferred activities. Candidates count: "
-                    + candidates.size());
-        }
-
-        final ArrayList<ResolveInfo> result =
-                filterCandidatesWithDomainPreferredActivitiesLPrBody(
-                        intent, matchFlags, candidates, xpDomainInfo, userId, debug);
-
-        if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
-            Slog.v(TAG, "Filtered results with preferred activities. New candidates count: "
-                    + result.size());
-            for (ResolveInfo info : result) {
-                Slog.v(TAG, "  + " + info.activityInfo);
-            }
-        }
-        return result;
-    }
-
     /**
      * Filter out activities with systemUserOnly flag set, when current user is not System.
      *
@@ -1930,63 +1758,6 @@
         return new ParceledListSlice<>(list);
     }
 
-    /**
-     * If the filter's target user can handle the intent and is enabled: a [ResolveInfo] that
-     * will forward the intent to the filter's target user, along with the highest approval of
-     * any handler in the target user. Otherwise, returns null.
-     */
-    @Nullable
-    private CrossProfileDomainInfo createForwardingResolveInfo(
-            @NonNull CrossProfileIntentFilter filter, @NonNull Intent intent,
-            @Nullable String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
-            int sourceUserId) {
-        int targetUserId = filter.getTargetUserId();
-        if (!isUserEnabled(targetUserId)) {
-            return null;
-        }
-
-        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent,
-                resolvedType, flags, targetUserId);
-        if (CollectionUtils.isEmpty(resultTargetUser)) {
-            return null;
-        }
-
-        ResolveInfo forwardingInfo = null;
-        for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
-            ResolveInfo targetUserResolveInfo = resultTargetUser.get(i);
-            if ((targetUserResolveInfo.activityInfo.applicationInfo.flags
-                    & ApplicationInfo.FLAG_SUSPENDED) == 0) {
-                forwardingInfo = createForwardingResolveInfoUnchecked(filter, sourceUserId,
-                        targetUserId);
-                break;
-            }
-        }
-
-        if (forwardingInfo == null) {
-            // If all the matches in the target profile are suspended, return null.
-            return null;
-        }
-
-        int highestApprovalLevel = DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
-
-        int size = resultTargetUser.size();
-        for (int i = 0; i < size; i++) {
-            ResolveInfo riTargetUser = resultTargetUser.get(i);
-            if (riTargetUser.handleAllWebDataURI) {
-                continue;
-            }
-            String packageName = riTargetUser.activityInfo.packageName;
-            PackageStateInternal ps = mSettings.getPackage(packageName);
-            if (ps == null) {
-                continue;
-            }
-            highestApprovalLevel = Math.max(highestApprovalLevel, mDomainVerificationManager
-                    .approvalLevelForDomain(ps, intent, flags, targetUserId));
-        }
-
-        return new CrossProfileDomainInfo(forwardingInfo, highestApprovalLevel);
-    }
-
     public final ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
             int sourceUserId, int targetUserId) {
         ResolveInfo forwardingResolveInfo = new ResolveInfo();
@@ -2022,83 +1793,6 @@
         return forwardingResolveInfo;
     }
 
-    // Return matching ResolveInfo in target user if any.
-    @Nullable
-    private CrossProfileDomainInfo queryCrossProfileIntents(
-            List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
-            long flags, int sourceUserId, boolean matchInCurrentProfile) {
-        if (matchingFilters == null) {
-            return null;
-        }
-        // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
-        // match the same intent. For performance reasons, it is better not to
-        // run queryIntent twice for the same userId
-        SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray();
-
-        CrossProfileDomainInfo resultInfo = null;
-
-        int size = matchingFilters.size();
-        for (int i = 0; i < size; i++) {
-            CrossProfileIntentFilter filter = matchingFilters.get(i);
-            int targetUserId = filter.getTargetUserId();
-            boolean skipCurrentProfile =
-                    (filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0;
-            boolean skipCurrentProfileIfNoMatchFound =
-                    (filter.getFlags() & PackageManager.ONLY_IF_NO_MATCH_FOUND) != 0;
-            if (!skipCurrentProfile && !alreadyTriedUserIds.get(targetUserId)
-                    && (!skipCurrentProfileIfNoMatchFound || !matchInCurrentProfile)) {
-                // Checking if there are activities in the target user that can handle the
-                // intent.
-                CrossProfileDomainInfo info = createForwardingResolveInfo(filter, intent,
-                        resolvedType, flags, sourceUserId);
-                if (info != null) {
-                    resultInfo = info;
-                    break;
-                }
-                alreadyTriedUserIds.put(targetUserId, true);
-            }
-        }
-
-        if (resultInfo == null) {
-            return null;
-        }
-
-        ResolveInfo forwardingResolveInfo = resultInfo.mResolveInfo;
-        if (!isUserEnabled(forwardingResolveInfo.targetUserId)) {
-            return null;
-        }
-
-        List<ResolveInfo> filteredResult =
-                filterIfNotSystemUser(Collections.singletonList(forwardingResolveInfo),
-                        sourceUserId);
-        if (filteredResult.isEmpty()) {
-            return null;
-        }
-
-        return resultInfo;
-    }
-
-    private ResolveInfo querySkipCurrentProfileIntents(
-            List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
-            long flags, int sourceUserId) {
-        if (matchingFilters != null) {
-            int size = matchingFilters.size();
-            for (int i = 0; i < size; i++) {
-                CrossProfileIntentFilter filter = matchingFilters.get(i);
-                if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
-                    // Checking if there are activities in the target user that can handle the
-                    // intent.
-                    CrossProfileDomainInfo info = createForwardingResolveInfo(filter, intent,
-                            resolvedType, flags, sourceUserId);
-                    if (info != null) {
-                        return info.mResolveInfo;
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
     public final ServiceInfo getServiceInfo(ComponentName component,
             @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         if (!mUserManager.exists(userId)) return null;
@@ -2740,16 +2434,6 @@
         }
     }
 
-    private boolean isUserEnabled(int userId) {
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            UserInfo userInfo = mUserManager.getUserInfo(userId);
-            return userInfo != null && userInfo.isEnabled();
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
     /**
      * Returns whether or not access to the application should be filtered.
      * <p>
@@ -5691,13 +5375,9 @@
             @UserIdInt int sourceUserId, @UserIdInt int targetUserId) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
-        List<CrossProfileIntentFilter> matches =
-                getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId);
-        if (matches != null) {
-            int size = matches.size();
-            for (int i = 0; i < size; i++) {
-                if (matches.get(i).getTargetUserId() == targetUserId) return true;
-            }
+        if (mCrossProfileIntentResolverEngine.canReachTo(this, intent, resolvedType,
+                sourceUserId, targetUserId)) {
+            return true;
         }
         if (intent.hasWebURI()) {
             // cross-profile app linking works only towards the parent.
diff --git a/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java b/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java
index 31f4fa3..72f3afc 100644
--- a/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java
+++ b/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java
@@ -16,12 +16,23 @@
 
 package com.android.server.pm;
 
+import android.annotation.UserIdInt;
 import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
 
 public final class CrossProfileDomainInfo {
     /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
     ResolveInfo mResolveInfo;
     int mHighestApprovalLevel;
+    @UserIdInt
+    int mTargetUserId = UserHandle.USER_CURRENT; // default as current user
+
+    CrossProfileDomainInfo(ResolveInfo resolveInfo, int highestApprovalLevel, @UserIdInt
+            int targetUserId) {
+        this.mResolveInfo = resolveInfo;
+        this.mHighestApprovalLevel = highestApprovalLevel;
+        this.mTargetUserId = targetUserId;
+    }
 
     CrossProfileDomainInfo(ResolveInfo resolveInfo, int highestApprovalLevel) {
         this.mResolveInfo = resolveInfo;
@@ -33,6 +44,7 @@
         return "CrossProfileDomainInfo{"
                 + "resolveInfo=" + mResolveInfo
                 + ", highestApprovalLevel=" + mHighestApprovalLevel
+                + ", targetUserId= " + mTargetUserId
                 + '}';
     }
 }
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java
new file mode 100644
index 0000000..3f82923
--- /dev/null
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) 2022 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.pm;
+
+import static android.content.pm.PackageManager.MATCH_ALL;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.server.LocalServices;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Rule based engine which decides strategy to be used for source,target pair and does cross profile
+ * intent resolution. Currently, we have only default and clone strategy. The major known use-case
+ * for default is work profile.
+ */
+public class CrossProfileIntentResolverEngine {
+
+    private final UserManagerService mUserManager;
+    private final DomainVerificationManagerInternal mDomainVerificationManager;
+    private final DefaultAppProvider mDefaultAppProvider;
+
+    public CrossProfileIntentResolverEngine(UserManagerService userManager,
+            DomainVerificationManagerInternal domainVerificationManager,
+            DefaultAppProvider defaultAppProvider) {
+        mUserManager = userManager;
+        mDomainVerificationManager = domainVerificationManager;
+        mDefaultAppProvider = defaultAppProvider;
+    }
+
+    /**
+     * Returns the list of {@link CrossProfileDomainInfo} which contains {@link ResolveInfo} from
+     * profiles linked directly/indirectly to user. Work-Owner as well as Clone-Owner
+     * are directly related as they are child of owner. Work-Clone are indirectly linked through
+     * owner profile.
+     * @param computer {@link Computer} instance used for resolution by {@link ComponentResolverApi}
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param userId source user for which intent request is called
+     * @param flags used for intent resolution
+     * @param pkgName the application package name this Intent is limited to.
+     * @param hasNonNegativePriorityResult signifies if current profile have any non-negative(active
+     *                                     and valid) ResolveInfo in current profile.
+     * @param pkgSettingFunction function to find PackageStateInternal for given package
+     * @return list of {@link CrossProfileDomainInfo} from linked profiles.
+     */
+    public List<CrossProfileDomainInfo> resolveIntent(@NonNull Computer computer, Intent intent,
+            String resolvedType, int userId, long flags, String pkgName,
+            boolean hasNonNegativePriorityResult,
+            Function<String, PackageStateInternal> pkgSettingFunction) {
+        return resolveIntentInternal(computer, intent, resolvedType, userId, flags, pkgName,
+                hasNonNegativePriorityResult, pkgSettingFunction);
+    }
+
+    /**
+     * Resolves intent in directly linked profiles and return list of {@link CrossProfileDomainInfo}
+     * which contains {@link ResolveInfo}. This would also recursively call profiles not directly
+     * linked.
+     *
+     * It first finds {@link CrossProfileIntentFilter} configured in current profile to find list of
+     * target user profiles that can serve current intent request. It uses corresponding strategy
+     * for each pair (source,target) user to resolve intent from target profile and returns combined
+     * results.
+     * @param computer {@link Computer} instance used for resolution by {@link ComponentResolverApi}
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param userId source user for which intent request is called
+     * @param flags used for intent resolution
+     * @param pkgName the application package name this Intent is limited to.
+     * @param hasNonNegativePriorityResult signifies if current profile have any non-negative(active
+     *                                     and valid) ResolveInfo in current profile.
+     * @param pkgSettingFunction function to find PackageStateInternal for given package
+     * @return list of {@link CrossProfileDomainInfo} from linked profiles.
+     */
+    private List<CrossProfileDomainInfo> resolveIntentInternal(@NonNull Computer computer,
+            Intent intent, String resolvedType, int userId, long flags, String pkgName,
+            boolean hasNonNegativePriorityResult,
+            Function<String, PackageStateInternal> pkgSettingFunction) {
+
+        List<CrossProfileDomainInfo> crossProfileDomainInfos = new ArrayList<>();
+
+        List<CrossProfileIntentFilter> matchingFilters =
+                computer.getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
+
+        if (matchingFilters == null || matchingFilters.isEmpty()) return crossProfileDomainInfos;
+
+        UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
+        UserInfo sourceUserInfo = umInternal.getUserInfo(userId);
+
+       // Grouping the CrossProfileIntentFilters based on targerId
+        SparseArray<List<CrossProfileIntentFilter>> crossProfileIntentFiltersByUser =
+                new SparseArray<>();
+
+        for (int index = 0; index < matchingFilters.size(); index++) {
+            CrossProfileIntentFilter crossProfileIntentFilter = matchingFilters.get(index);
+
+            if (!crossProfileIntentFiltersByUser
+                    .contains(crossProfileIntentFilter.mTargetUserId)) {
+                crossProfileIntentFiltersByUser.put(crossProfileIntentFilter.mTargetUserId,
+                        new ArrayList<>());
+            }
+            crossProfileIntentFiltersByUser.get(crossProfileIntentFilter.mTargetUserId)
+                    .add(crossProfileIntentFilter);
+        }
+
+        /*
+         For each target user, we would call their corresponding strategy
+         {@link CrossProfileResolver} to resolve intent in corresponding user
+         */
+        for (int index = 0; index < crossProfileIntentFiltersByUser.size(); index++) {
+
+            UserInfo targetUserInfo = umInternal.getUserInfo(crossProfileIntentFiltersByUser
+                    .keyAt(index));
+
+            // Choosing strategy based on source and target user
+            CrossProfileResolver crossProfileResolver =
+                    chooseCrossProfileResolver(computer, sourceUserInfo, targetUserInfo);
+
+            /*
+            If {@link CrossProfileResolver} is available for source,target pair we will call it to
+            get {@link CrossProfileDomainInfo}s from that user.
+             */
+            if (crossProfileResolver != null) {
+                List<CrossProfileDomainInfo> crossProfileInfos = crossProfileResolver
+                        .resolveIntent(computer, intent, resolvedType, userId,
+                                crossProfileIntentFiltersByUser.keyAt(index), flags, pkgName,
+                                crossProfileIntentFiltersByUser.valueAt(index),
+                                hasNonNegativePriorityResult, pkgSettingFunction);
+                crossProfileDomainInfos.addAll(crossProfileInfos);
+            }
+        }
+
+        return crossProfileDomainInfos;
+    }
+
+
+    /**
+     * Returns {@link CrossProfileResolver} strategy based on source and target user
+     * @param computer {@link Computer} instance used for resolution by {@link ComponentResolverApi}
+     * @param sourceUserInfo source user
+     * @param targetUserInfo target user
+     * @return {@code CrossProfileResolver} which has value if source and target have
+     * strategy configured otherwise null.
+     */
+    @SuppressWarnings("unused")
+    private CrossProfileResolver chooseCrossProfileResolver(@NonNull Computer computer,
+            UserInfo sourceUserInfo, UserInfo targetUserInfo) {
+        return new DefaultCrossProfileResolver(computer.getComponentResolver(),
+                mUserManager, mDomainVerificationManager);
+    }
+
+    /**
+     * Returns true if we source user can reach target user for given intent. The source can
+     * directly or indirectly reach to target.
+     * @param computer {@link Computer} instance used for resolution by {@link ComponentResolverApi}
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param sourceUserId source user
+     * @param targetUserId target user
+     * @return true if we source user can reach target user for given intent
+     */
+    public boolean canReachTo(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, @UserIdInt int sourceUserId,
+            @UserIdInt int targetUserId) {
+        return canReachToInternal(computer, intent, resolvedType, sourceUserId, targetUserId);
+    }
+
+    /**
+     * Returns true if we source user can reach target user for given intent. The source can
+     * directly or indirectly reach to target. This will perform depth first search to check if
+     * source can reach target.
+     * @param computer {@link Computer} instance used for resolution by {@link ComponentResolverApi}
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param sourceUserId source user
+     * @param targetUserId target user
+     * @return true if we source user can reach target user for given intent
+     */
+    private boolean canReachToInternal(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, @UserIdInt int sourceUserId,
+            @UserIdInt int targetUserId) {
+        if (sourceUserId == targetUserId) return true;
+
+        List<CrossProfileIntentFilter> matches =
+                computer.getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId);
+        if (matches != null) {
+            for (int index = 0; index < matches.size(); index++) {
+                CrossProfileIntentFilter crossProfileIntentFilter = matches.get(index);
+                if (crossProfileIntentFilter.mTargetUserId == targetUserId) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks if any of the matching {@link CrossProfileIntentFilter} suggest we should skip the
+     * current profile based on flag {@link PackageManager#SKIP_CURRENT_PROFILE}.
+     * @param computer {@link Computer} instance used to find {@link CrossProfileIntentFilter}
+     *                                 for user
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param sourceUserId id of initiating user space
+     * @return boolean if we should skip resolution in current/source profile.
+     */
+    public boolean shouldSkipCurrentProfile(Computer computer, Intent intent, String resolvedType,
+            int sourceUserId) {
+        List<CrossProfileIntentFilter> matches =
+                computer.getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId);
+        if (matches != null) {
+            for (int matchIndex = 0; matchIndex < matches.size(); matchIndex++) {
+                CrossProfileIntentFilter crossProfileIntentFilter = matches.get(matchIndex);
+                if ((crossProfileIntentFilter.getFlags()
+                        & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Combines result from current and cross profile. This also does filtering based on domain(if
+     * required).
+     * @param computer {@link Computer} instance
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param instantAppPkgName package name if instant app is allowed
+     * @param pkgName the application package name this Intent is limited to.
+     * @param allowDynamicSplits true if dynamic splits is allowed
+     * @param matchFlags flags for intent request
+     * @param userId user id of source user
+     * @param filterCallingUid uid of calling process
+     * @param resolveForStart true if resolution occurs because an application is starting
+     * @param candidates resolveInfos from current profile
+     * @param crossProfileCandidates crossProfileDomainInfos from cross profile, it has ResolveInfo
+     * @param areWebInstantAppsDisabled true if web instant apps are disabled
+     * @param addInstant true if instant apps are allowed
+     * @param sortResult true if caller would need to sort the results
+     * @param pkgSettingFunction function to find PackageStateInternal for given package
+     * @return QueryIntentActivitiesResult which contains resolveInfos
+     */
+    public QueryIntentActivitiesResult combineFilterAndCreateQueryActivitiesResponse(
+            Computer computer, Intent intent, String resolvedType, String instantAppPkgName,
+            String pkgName, boolean allowDynamicSplits, long matchFlags, int userId,
+            int filterCallingUid, boolean resolveForStart, List<ResolveInfo> candidates,
+            List<CrossProfileDomainInfo> crossProfileCandidates, boolean areWebInstantAppsDisabled,
+            boolean addInstant, boolean sortResult,
+            Function<String, PackageStateInternal> pkgSettingFunction) {
+
+        if (shouldSkipCurrentProfile(computer, intent, resolvedType, userId)) {
+            /*
+             if current profile is skipped return results from cross profile after filtering
+             ephemeral activities.
+             */
+            candidates = resolveInfoFromCrossProfileDomainInfo(crossProfileCandidates);
+
+            return new QueryIntentActivitiesResult(computer.applyPostResolutionFilter(candidates,
+                    instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,
+                    userId, intent));
+        }
+
+        if (pkgName == null && intent.hasWebURI()) {
+            // If instant apps are not allowed and there is result only from current or cross
+            // profile return it
+            if (!addInstant && ((candidates.size() <= 1 && crossProfileCandidates.isEmpty())
+                    || (candidates.isEmpty() && !crossProfileCandidates.isEmpty()))) {
+                candidates.addAll(resolveInfoFromCrossProfileDomainInfo(crossProfileCandidates));
+                return new QueryIntentActivitiesResult(computer.applyPostResolutionFilter(
+                        candidates, instantAppPkgName, allowDynamicSplits, filterCallingUid,
+                        resolveForStart, userId, intent));
+            }
+            /*
+             if there are multiple results from current and cross profile, combining and filtering
+             results based on domain priority.
+             */
+            candidates = filterCandidatesWithDomainPreferredActivitiesLPr(computer, intent,
+                    matchFlags, candidates, crossProfileCandidates, userId,
+                    areWebInstantAppsDisabled, pkgSettingFunction);
+        } else {
+            candidates.addAll(resolveInfoFromCrossProfileDomainInfo(crossProfileCandidates));
+        }
+
+        return new QueryIntentActivitiesResult(sortResult, addInstant, candidates);
+    }
+
+    /**
+     * It filters and combines results from current and cross profile based on domain priority.
+     * @param computer {@link Computer} instance
+     * @param intent request
+     * @param matchFlags flags for intent request
+     * @param candidates resolveInfos from current profile
+     * @param crossProfileCandidates crossProfileDomainInfos from cross profile, it have ResolveInfo
+     * @param userId user id of source user
+     * @param areWebInstantAppsDisabled true if web instant apps are disabled
+     * @param pkgSettingFunction function to find PackageStateInternal for given package
+     * @return list of ResolveInfo
+     */
+    private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Computer computer,
+            Intent intent, long matchFlags, List<ResolveInfo> candidates,
+            List<CrossProfileDomainInfo> crossProfileCandidates, int userId,
+            boolean areWebInstantAppsDisabled,
+            Function<String, PackageStateInternal> pkgSettingFunction) {
+        final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
+
+        if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
+            Slog.v(TAG, "Filtering results with preferred activities. Candidates count: "
+                    + candidates.size());
+        }
+
+        final List<ResolveInfo> result =
+                filterCandidatesWithDomainPreferredActivitiesLPrBody(computer, intent, matchFlags,
+                        candidates, crossProfileCandidates, userId, areWebInstantAppsDisabled,
+                        debug, pkgSettingFunction);
+
+        if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
+            Slog.v(TAG, "Filtered results with preferred activities. New candidates count: "
+                    + result.size());
+            for (ResolveInfo info : result) {
+                Slog.v(TAG, " + " + info.activityInfo);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Filters candidates satisfying domain criteria.
+     * @param computer {@link Computer} instance
+     * @param intent request
+     * @param matchFlags flags for intent request
+     * @param candidates resolveInfos from current profile
+     * @param crossProfileCandidates crossProfileDomainInfos from cross profile, it have ResolveInfo
+     * @param userId user id of source user
+     * @param areWebInstantAppsDisabled true if web instant apps are disabled
+     * @param debug true if resolution logs needed to be printed
+     * @param pkgSettingFunction function to find PackageStateInternal for given package
+     * @return list of resolve infos
+     */
+    private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
+            Computer computer, Intent intent, long matchFlags, List<ResolveInfo> candidates,
+            List<CrossProfileDomainInfo> crossProfileCandidates, int userId,
+            boolean areWebInstantAppsDisabled, boolean debug,
+            Function<String, PackageStateInternal> pkgSettingFunction) {
+        final ArrayList<ResolveInfo> result = new ArrayList<>();
+        final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
+        final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
+
+        // Blocking instant apps is usually done in applyPostResolutionFilter, but since
+        // domain verification can resolve to a single result, which can be an instant app,
+        // it will then be filtered to an empty list in that method. Instead, do blocking
+        // here so that instant apps can be ignored for approval filtering and a lower
+        // priority result chosen instead.
+        final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled;
+
+        final int count = candidates.size();
+        // First, try to use approved apps.
+        for (int n = 0; n < count; n++) {
+            ResolveInfo info = candidates.get(n);
+            if (blockInstant && (info.isInstantAppAvailable
+                    || computer.isInstantAppInternal(info.activityInfo.packageName, userId,
+                    Process.SYSTEM_UID))) {
+                continue;
+            }
+
+            // Add to the special match all list (Browser use case)
+            if (info.handleAllWebDataURI) {
+                matchAllList.add(info);
+            } else {
+                undefinedList.add(info);
+            }
+        }
+
+        // We'll want to include browser possibilities in a few cases
+        boolean includeBrowser = false;
+
+        /**
+         * Grouping CrossProfileDomainInfo based on target user
+         */
+        SparseArray<List<CrossProfileDomainInfo>> categorizeResolveInfoByTargetUser =
+                new SparseArray<>();
+        if (crossProfileCandidates != null && !crossProfileCandidates.isEmpty()) {
+            for (int index = 0; index < crossProfileCandidates.size(); index++) {
+                CrossProfileDomainInfo crossProfileDomainInfo = crossProfileCandidates.get(index);
+                if (!categorizeResolveInfoByTargetUser
+                        .contains(crossProfileDomainInfo.mTargetUserId)) {
+                    categorizeResolveInfoByTargetUser.put(crossProfileDomainInfo.mTargetUserId,
+                            new ArrayList<>());
+                }
+                categorizeResolveInfoByTargetUser.get(crossProfileDomainInfo.mTargetUserId)
+                        .add(crossProfileDomainInfo);
+            }
+        }
+
+        if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) {
+            result.addAll(undefinedList);
+
+            // calling cross profile strategy to filter corresponding results
+            result.addAll(filterCrossProfileCandidatesWithDomainPreferredActivities(computer,
+                    intent, matchFlags, categorizeResolveInfoByTargetUser, userId,
+                    DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE));
+            includeBrowser = true;
+        } else {
+            Pair<List<ResolveInfo>, Integer> infosAndLevel = mDomainVerificationManager
+                    .filterToApprovedApp(intent, undefinedList, userId, pkgSettingFunction);
+            List<ResolveInfo> approvedInfos = infosAndLevel.first;
+            Integer highestApproval = infosAndLevel.second;
+
+            // If no apps are approved for the domain, resolve only to browsers
+            if (approvedInfos.isEmpty()) {
+                includeBrowser = true;
+                // calling cross profile strategy to filter corresponding results
+                result.addAll(filterCrossProfileCandidatesWithDomainPreferredActivities(computer,
+                        intent, matchFlags, categorizeResolveInfoByTargetUser, userId,
+                        DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE));
+            } else {
+                result.addAll(approvedInfos);
+
+                // If the other profile has an app that's higher approval, add it
+                // calling cross profile strategy to filter corresponding results
+                result.addAll(filterCrossProfileCandidatesWithDomainPreferredActivities(computer,
+                        intent, matchFlags, categorizeResolveInfoByTargetUser, userId,
+                        highestApproval));
+            }
+        }
+
+        if (includeBrowser) {
+            // Also add browsers (all of them or only the default one)
+            if (DEBUG_DOMAIN_VERIFICATION) {
+                Slog.v(TAG, " ...including browsers in candidate set");
+            }
+            if ((matchFlags & MATCH_ALL) != 0) {
+                result.addAll(matchAllList);
+            } else {
+                // Browser/generic handling case. If there's a default browser, go straight
+                // to that (but only if there is no other higher-priority match).
+                final String defaultBrowserPackageName = mDefaultAppProvider.getDefaultBrowser(
+                        userId);
+                int maxMatchPrio = 0;
+                ResolveInfo defaultBrowserMatch = null;
+                final int numCandidates = matchAllList.size();
+                for (int n = 0; n < numCandidates; n++) {
+                    ResolveInfo info = matchAllList.get(n);
+                    // track the highest overall match priority...
+                    if (info.priority > maxMatchPrio) {
+                        maxMatchPrio = info.priority;
+                    }
+                    // ...and the highest-priority default browser match
+                    if (info.activityInfo.packageName.equals(defaultBrowserPackageName)) {
+                        if (defaultBrowserMatch == null
+                                || (defaultBrowserMatch.priority < info.priority)) {
+                            if (debug) {
+                                Slog.v(TAG, "Considering default browser match " + info);
+                            }
+                            defaultBrowserMatch = info;
+                        }
+                    }
+                }
+                if (defaultBrowserMatch != null
+                        && defaultBrowserMatch.priority >= maxMatchPrio
+                        && !TextUtils.isEmpty(defaultBrowserPackageName)) {
+                    if (debug) {
+                        Slog.v(TAG, "Default browser match " + defaultBrowserMatch);
+                    }
+                    result.add(defaultBrowserMatch);
+                } else {
+                    result.addAll(matchAllList);
+                }
+            }
+
+            // If there is nothing selected, add all candidates
+            if (result.size() == 0) {
+                result.addAll(candidates);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Filter cross profile results by calling their respective strategy
+     * @param computer {@link Computer} instance
+     * @param intent request
+     * @param flags for intent request
+     * @param categorizeResolveInfoByTargetUser group of targetuser and its corresponding
+     *                                          CrossProfileDomainInfos
+     * @param sourceUserId user id for intent
+     * @param highestApprovalLevel domain approval level
+     * @return list of ResolveInfos
+     */
+    private List<ResolveInfo> filterCrossProfileCandidatesWithDomainPreferredActivities(
+            Computer computer, Intent intent, long flags, SparseArray<List<CrossProfileDomainInfo>>
+            categorizeResolveInfoByTargetUser, int sourceUserId, int highestApprovalLevel) {
+
+        List<CrossProfileDomainInfo> crossProfileDomainInfos = new ArrayList<>();
+        UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
+        UserInfo sourceUserInfo = umInternal.getUserInfo(sourceUserId);
+
+        for (int index = 0; index < categorizeResolveInfoByTargetUser.size(); index++) {
+
+            // if resolve info does not target user or has default value, add results as they are.
+            if (categorizeResolveInfoByTargetUser.keyAt(index) == -2) {
+                crossProfileDomainInfos.addAll(categorizeResolveInfoByTargetUser.valueAt(index));
+            } else {
+                // finding cross profile strategy based on source and target user
+                CrossProfileResolver crossProfileIntentResolver =
+                        chooseCrossProfileResolver(computer, sourceUserInfo, umInternal
+                                .getUserInfo(categorizeResolveInfoByTargetUser.keyAt(index)));
+                // if strategy is available call it and add its filtered results
+                if (crossProfileIntentResolver != null) {
+                    crossProfileDomainInfos.addAll(crossProfileIntentResolver
+                            .filterResolveInfoWithDomainPreferredActivity(intent,
+                                    categorizeResolveInfoByTargetUser.valueAt(index),
+                                    flags, sourceUserId, categorizeResolveInfoByTargetUser
+                                            .keyAt(index), highestApprovalLevel));
+                } else {
+                    // if strategy is not available call it, add the results
+                    crossProfileDomainInfos.addAll(categorizeResolveInfoByTargetUser
+                            .valueAt(index));
+                }
+            }
+        }
+        return resolveInfoFromCrossProfileDomainInfo(crossProfileDomainInfos);
+    }
+
+    /**
+     * Extract ResolveInfo from CrossProfileDomainInfo
+     * @param crossProfileDomainInfos cross profile results
+     * @return list of ResolveInfo
+     */
+    private List<ResolveInfo> resolveInfoFromCrossProfileDomainInfo(List<CrossProfileDomainInfo>
+            crossProfileDomainInfos) {
+        List<ResolveInfo> resolveInfoList = new ArrayList<>();
+
+        for (int infoIndex = 0; infoIndex < crossProfileDomainInfos.size(); infoIndex++) {
+            resolveInfoList.add(crossProfileDomainInfos.get(infoIndex).mResolveInfo);
+        }
+
+        return resolveInfoList;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/CrossProfileResolver.java b/services/core/java/com/android/server/pm/CrossProfileResolver.java
new file mode 100644
index 0000000..a8da818
--- /dev/null
+++ b/services/core/java/com/android/server/pm/CrossProfileResolver.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 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.pm;
+
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.os.UserHandle;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.resolution.ComponentResolverApi;
+
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Abstract Class act as base class for Cross Profile strategy.
+ * This will be used by {@link CrossProfileIntentResolverEngine} to resolve intent across profile.
+ */
+public abstract class CrossProfileResolver {
+
+    protected ComponentResolverApi mComponentResolver;
+    protected UserManagerService mUserManager;
+
+    public CrossProfileResolver(ComponentResolverApi componentResolver,
+            UserManagerService userManager) {
+        mComponentResolver = componentResolver;
+        mUserManager = userManager;
+    }
+
+    /**
+     * This method would be overridden by concrete implementation. This method should define how to
+     * resolve given intent request in target profile.
+     * @param computer ComputerEngine instance that would be needed by ComponentResolverApi
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param userId source/initiating user
+     * @param targetUserId target user id
+     * @param flags of intent request
+     * @param pkgName package name if defined.
+     * @param matchingFilters {@link CrossProfileIntentFilter}s configured for source user,
+     *                                                        targeting the targetUserId
+     * @param hasNonNegativePriorityResult if source have any non-negative(active and valid)
+     *                                     resolveInfo in their profile.
+     * @param pkgSettingFunction function to find PackageStateInternal for given package
+     * @return list of {@link CrossProfileDomainInfo}
+     */
+    public abstract List<CrossProfileDomainInfo> resolveIntent(Computer computer, Intent intent,
+            String resolvedType, int userId, int targetUserId, long flags,
+            String pkgName, List<CrossProfileIntentFilter> matchingFilters,
+            boolean hasNonNegativePriorityResult,
+            Function<String, PackageStateInternal> pkgSettingFunction);
+
+    /**
+     * Filters the CrossProfileDomainInfos, the filtering technique would be defined by concrete
+     * implementation class
+     * @param intent request
+     * @param crossProfileDomainInfos resolved in target user
+     * @param flags for intent resolution
+     * @param sourceUserId source user
+     * @param targetUserId target user
+     * @param highestApprovalLevel highest level of domain approval
+     * @return filtered list of {@link CrossProfileDomainInfo}
+     */
+    public abstract List<CrossProfileDomainInfo> filterResolveInfoWithDomainPreferredActivity(
+            Intent intent, List<CrossProfileDomainInfo> crossProfileDomainInfos, long flags,
+            int sourceUserId, int targetUserId, int highestApprovalLevel);
+
+    /**
+     * Checks if mentioned user is enabled
+     * @param userId of requested user
+     * @return true if user is enabled
+     */
+    protected final boolean isUserEnabled(int userId) {
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            UserInfo userInfo = mUserManager.getUserInfo(userId);
+            return userInfo != null && userInfo.isEnabled();
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    /**
+     * Filters out {@link CrossProfileDomainInfo} if they are not for any user apart from system
+     * user. If mentioned user is system user, then returns all responses.
+     * @param crossProfileDomainInfos result from resolution
+     * @param userId source user id
+     * @return filtered list of {@link CrossProfileDomainInfo}
+     */
+    protected final List<CrossProfileDomainInfo> filterIfNotSystemUser(
+            List<CrossProfileDomainInfo> crossProfileDomainInfos, int userId) {
+        if (userId == UserHandle.USER_SYSTEM) {
+            return crossProfileDomainInfos;
+        }
+
+        for (int i = CollectionUtils.size(crossProfileDomainInfos) - 1; i >= 0; i--) {
+            ResolveInfo info = crossProfileDomainInfos.get(i).mResolveInfo;
+            if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
+                crossProfileDomainInfos.remove(i);
+            }
+        }
+        return crossProfileDomainInfos;
+    }
+
+    /**
+     * Returns user info of parent profile is applicable
+     * @param userId requested user
+     * @return parent's user info, null if parent is not present
+     */
+    protected final UserInfo getProfileParent(int userId) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mUserManager.getProfileParent(userId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java b/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java
new file mode 100644
index 0000000..90d89c6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2022 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.app.IntentForwarderActivity;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.resolution.ComponentResolverApi;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Cross profile resolver used as default strategy. Primary known use-case for this resolver is
+ * work/managed profile .
+ */
+public final class DefaultCrossProfileResolver extends CrossProfileResolver {
+
+    private final DomainVerificationManagerInternal mDomainVerificationManager;
+
+
+    public DefaultCrossProfileResolver(ComponentResolverApi componentResolver,
+            UserManagerService userManager,
+            DomainVerificationManagerInternal domainVerificationManager) {
+        super(componentResolver, userManager);
+        mDomainVerificationManager = domainVerificationManager;
+    }
+
+    /**
+     * This is Default resolution strategy primarily used by Work Profile.
+     * First, it checks if we have to skip source profile and just resolve in target profile. If
+     * yes, then it will return result from target profile.
+     * Secondly, it find specific resolve infos in target profile
+     * Thirdly, if it is web intent it finds if parent can also resolve it. The results of this
+     * stage gets higher priority as compared to second stage.
+     *
+     * @param computer ComputerEngine instance that would be needed by ComponentResolverApi
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param userId source/initiating user
+     * @param targetUserId target user id
+     * @param flags of intent request
+     * @param pkgName the application package name this Intent is limited to.
+     * @param matchingFilters {@link CrossProfileIntentFilter}s configured for source user,
+     *                                                        targeting the targetUserId
+     * @param hasNonNegativePriorityResult if source have any non-negative(active and valid)
+     *                                     resolveInfo in their profile.
+     * @param pkgSettingFunction function to find PackageStateInternal for given package
+     * @return list of {@link CrossProfileDomainInfo}
+     */
+    @Override
+    public List<CrossProfileDomainInfo> resolveIntent(Computer computer, Intent intent,
+            String resolvedType, int userId, int targetUserId,
+            long flags, String pkgName, List<CrossProfileIntentFilter> matchingFilters,
+            boolean hasNonNegativePriorityResult,
+            Function<String, PackageStateInternal> pkgSettingFunction) {
+
+        List<CrossProfileDomainInfo> xpResult = new ArrayList<>();
+        if (pkgName != null) return xpResult;
+        CrossProfileDomainInfo skipProfileInfo = querySkipCurrentProfileIntents(computer,
+                matchingFilters, intent, resolvedType, flags, userId, pkgSettingFunction);
+
+        if (skipProfileInfo != null) {
+            xpResult.add(skipProfileInfo);
+            return filterIfNotSystemUser(xpResult, userId);
+        }
+
+        CrossProfileDomainInfo specificXpInfo = queryCrossProfileIntents(computer,
+                matchingFilters, intent, resolvedType, flags, userId,
+                hasNonNegativePriorityResult, pkgSettingFunction);
+
+        if (intent.hasWebURI()) {
+            CrossProfileDomainInfo generalXpInfo = null;
+            final UserInfo parent = getProfileParent(userId);
+            if (parent != null) {
+                generalXpInfo = computer.getCrossProfileDomainPreferredLpr(intent, resolvedType,
+                        flags, userId, parent.id);
+            }
+            CrossProfileDomainInfo prioritizedXpInfo =
+                    generalXpInfo != null ? generalXpInfo : specificXpInfo;
+            if (prioritizedXpInfo != null) {
+                xpResult.add(prioritizedXpInfo);
+            }
+        } else if (specificXpInfo != null) {
+            xpResult.add(specificXpInfo);
+        }
+
+        return xpResult;
+    }
+
+    /**
+     * Filters out CrossProfileDomainInfo if it does not have higher approval level as compared to
+     * given approval level
+     * @param intent request
+     * @param crossProfileDomainInfos resolved in target user
+     * @param flags for intent resolution
+     * @param sourceUserId source user
+     * @param targetUserId target user
+     * @param highestApprovalLevel highest level of domain approval
+     * @return filtered list of CrossProfileDomainInfo
+     */
+    @Override
+    public List<CrossProfileDomainInfo> filterResolveInfoWithDomainPreferredActivity(
+            Intent intent, List<CrossProfileDomainInfo> crossProfileDomainInfos, long flags,
+            int sourceUserId, int targetUserId, int highestApprovalLevel) {
+
+        List<CrossProfileDomainInfo> filteredCrossProfileDomainInfos = new ArrayList<>();
+
+        if (crossProfileDomainInfos != null && !crossProfileDomainInfos.isEmpty()) {
+            for (int index = 0; index < crossProfileDomainInfos.size(); index++) {
+                CrossProfileDomainInfo crossProfileDomainInfo = crossProfileDomainInfos.get(index);
+                if (crossProfileDomainInfo.mHighestApprovalLevel > highestApprovalLevel) {
+                    filteredCrossProfileDomainInfos.add(crossProfileDomainInfo);
+                }
+            }
+        }
+
+        return filteredCrossProfileDomainInfos;
+    }
+
+    /**
+     * If current/source profile needs to be skipped, returns CrossProfileDomainInfo from target
+     * profile. If any of the matchingFilters have flag {@link PackageManager#SKIP_CURRENT_PROFILE}
+     * set that would signify that current profile needs to be skipped.
+     * @param computer ComputerEngine instance that would be needed by ComponentResolverApi
+     * @param matchingFilters {@link CrossProfileIntentFilter}s configured for source user,
+     *                                                        targeting the targetUserId
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param flags for intent resolution
+     * @param sourceUserId source user
+     * @param pkgSettingFunction function to find PackageStateInternal for given package
+     * @return CrossProfileDomainInfo if current profile needs to be skipped, else null
+     */
+    @Nullable
+    private CrossProfileDomainInfo querySkipCurrentProfileIntents(Computer computer,
+            List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
+            long flags, int sourceUserId,
+            Function<String, PackageStateInternal> pkgSettingFunction) {
+        if (matchingFilters != null) {
+            int size = matchingFilters.size();
+            for (int i = 0; i < size; i++) {
+                CrossProfileIntentFilter filter = matchingFilters.get(i);
+                if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
+                    // Checking if there are activities in the target user that can handle the
+                    // intent.
+                    CrossProfileDomainInfo info = createForwardingResolveInfo(computer, filter,
+                            intent, resolvedType, flags, sourceUserId, pkgSettingFunction);
+                    if (info != null) {
+                        return info;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Resolves and returns CrossProfileDomainInfo(ForwardingResolveInfo) from target profile if
+     * current profile should be skipped when there is no result or if target profile should not
+     * be skipped.
+     *
+     * @param computer ComputerEngine instance that would be needed by ComponentResolverApi
+     * @param matchingFilters {@link CrossProfileIntentFilter}s configured for source user,
+     *                                                        targeting the targetUserId
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param flags for intent resolution
+     * @param sourceUserId source user
+     * @param matchInCurrentProfile true if current/source profile have some non-negative
+     *                              resolveInfo
+     * @param pkgSettingFunction function to find PackageStateInternal for given package
+     * @return CrossProfileDomainInfo returns forwarding intent resolver in CrossProfileDomainInfo.
+     * It returns null if there are no matching filters or no valid/active activity available
+     */
+    @Nullable
+    private CrossProfileDomainInfo queryCrossProfileIntents(Computer computer,
+            List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
+            long flags, int sourceUserId, boolean matchInCurrentProfile,
+            Function<String, PackageStateInternal> pkgSettingFunction) {
+        if (matchingFilters == null) {
+            return null;
+        }
+        // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
+        // match the same intent. For performance reasons, it is better not to
+        // run queryIntent twice for the same userId
+        SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray();
+
+        CrossProfileDomainInfo resultInfo = null;
+
+        int size = matchingFilters.size();
+        for (int i = 0; i < size; i++) {
+            CrossProfileIntentFilter filter = matchingFilters.get(i);
+            int targetUserId = filter.getTargetUserId();
+            boolean skipCurrentProfile =
+                    (filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0;
+            boolean skipCurrentProfileIfNoMatchFound =
+                    (filter.getFlags() & PackageManager.ONLY_IF_NO_MATCH_FOUND) != 0;
+            if (!skipCurrentProfile && !alreadyTriedUserIds.get(targetUserId)
+                    && (!skipCurrentProfileIfNoMatchFound || !matchInCurrentProfile)) {
+                // Checking if there are activities in the target user that can handle the
+                // intent.
+                CrossProfileDomainInfo info = createForwardingResolveInfo(computer, filter, intent,
+                        resolvedType, flags, sourceUserId, pkgSettingFunction);
+                if (info != null) {
+                    resultInfo = info;
+                    break;
+                }
+                alreadyTriedUserIds.put(targetUserId, true);
+            }
+        }
+
+        if (resultInfo == null) {
+            return null;
+        }
+
+        ResolveInfo forwardingResolveInfo = resultInfo.mResolveInfo;
+        if (!isUserEnabled(forwardingResolveInfo.targetUserId)) {
+            return null;
+        }
+
+        List<CrossProfileDomainInfo> filteredResult =
+                filterIfNotSystemUser(Collections.singletonList(resultInfo), sourceUserId);
+        if (filteredResult.isEmpty()) {
+            return null;
+        }
+
+        return resultInfo;
+    }
+
+    /**
+     * Creates a Forwarding Resolve Info, used when we have to signify that target profile's
+     * resolveInfo should be considered without providing list of resolve infos.
+     * @param computer ComputerEngine instance that would be needed by ComponentResolverApi
+     * @param filter {@link CrossProfileIntentFilter} configured for source user,
+     *                                                        targeting the targetUserId
+     * @param intent request
+     * @param resolvedType the MIME data type of intent request
+     * @param flags for intent resolution
+     * @param sourceUserId source user
+     * @return CrossProfileDomainInfo whose ResolveInfo is forwarding. It would be resolved by
+     * {@link IntentForwarderActivity}. It returns null if there are no valid/active activities
+     */
+    @Nullable
+    protected CrossProfileDomainInfo createForwardingResolveInfo(Computer computer,
+            @NonNull CrossProfileIntentFilter filter, @NonNull Intent intent,
+            @Nullable String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
+            int sourceUserId, @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
+        int targetUserId = filter.getTargetUserId();
+        if (!isUserEnabled(targetUserId)) {
+            return null;
+        }
+
+        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(computer, intent,
+                resolvedType, flags, targetUserId);
+        if (CollectionUtils.isEmpty(resultTargetUser)) {
+            return null;
+        }
+
+        ResolveInfo forwardingInfo = null;
+        for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
+            ResolveInfo targetUserResolveInfo = resultTargetUser.get(i);
+            if ((targetUserResolveInfo.activityInfo.applicationInfo.flags
+                    & ApplicationInfo.FLAG_SUSPENDED) == 0) {
+                forwardingInfo = computer.createForwardingResolveInfoUnchecked(filter, sourceUserId,
+                        targetUserId);
+                break;
+            }
+        }
+
+        if (forwardingInfo == null) {
+            // If all the matches in the target profile are suspended, return null.
+            return null;
+        }
+
+        int highestApprovalLevel = DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
+
+        int size = resultTargetUser.size();
+        for (int i = 0; i < size; i++) {
+            ResolveInfo riTargetUser = resultTargetUser.get(i);
+            if (riTargetUser.handleAllWebDataURI) {
+                continue;
+            }
+            String packageName = riTargetUser.activityInfo.packageName;
+            PackageStateInternal ps = pkgSettingFunction.apply(packageName);
+            if (ps == null) {
+                continue;
+            }
+            highestApprovalLevel = Math.max(highestApprovalLevel, mDomainVerificationManager
+                    .approvalLevelForDomain(ps, intent, flags, targetUserId));
+        }
+
+        return new CrossProfileDomainInfo(forwardingInfo, highestApprovalLevel, targetUserId);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index d2a5e32..3385a09 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -26,6 +26,7 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.incremental.PerUidReadTimeouts;
 import android.service.pm.PackageServiceDumpProto;
@@ -60,6 +61,7 @@
     private final ArrayMap<String, FeatureInfo> mAvailableFeatures;
     private final ArraySet<String> mProtectedBroadcasts;
     private final PerUidReadTimeouts[] mPerUidReadTimeouts;
+    private final SnapshotStatistics mSnapshotStatistics;
 
     DumpHelper(
             PermissionManagerServiceInternal permissionManager,
@@ -70,7 +72,8 @@
             ChangedPackagesTracker changedPackagesTracker,
             ArrayMap<String, FeatureInfo> availableFeatures,
             ArraySet<String> protectedBroadcasts,
-            PerUidReadTimeouts[] perUidReadTimeouts) {
+            PerUidReadTimeouts[] perUidReadTimeouts,
+            SnapshotStatistics snapshotStatistics) {
         mPermissionManager = permissionManager;
         mStorageEventHelper = storageEventHelper;
         mDomainVerificationManager = domainVerificationManager;
@@ -81,6 +84,7 @@
         mAvailableFeatures = availableFeatures;
         mProtectedBroadcasts = protectedBroadcasts;
         mPerUidReadTimeouts = perUidReadTimeouts;
+        mSnapshotStatistics = snapshotStatistics;
     }
 
     @NeverCompile // Avoid size overhead of debugging code.
@@ -585,6 +589,9 @@
             if (dumpState.onTitlePrinted()) {
                 pw.println();
             }
+            pw.println("Snapshot statistics:");
+            mSnapshotStatistics.dump(pw, "   " /* indent */, SystemClock.currentTimeMicro(),
+                    snapshot.getUsed(), dumpState.isBrief());
         }
 
         if (!checkin
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index e9f26e9..022bf3c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -81,9 +81,11 @@
 import android.content.pm.InstallationFileParcel;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.PreapprovalDetails;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.PackageInfoFlags;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.SigningDetails;
 import android.content.pm.dex.DexMetadataHelper;
@@ -92,8 +94,13 @@
 import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.icu.util.ULocale;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -193,6 +200,7 @@
     private static final int MSG_INSTALL = 3;
     private static final int MSG_ON_PACKAGE_INSTALLED = 4;
     private static final int MSG_SESSION_VALIDATION_FAILURE = 5;
+    private static final int MSG_PRE_APPROVAL_REQUEST = 6;
 
     /** XML constants used for persisting a session */
     static final String TAG_SESSION = "session";
@@ -275,7 +283,7 @@
      * #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#R} before getting the
      * target sdk version from a validated apk in {@link #validateApkInstallLocked()}, the compared
      * result will not trigger any user action in
-     * {@link #checkUserActionRequirement(PackageInstallerSession)}.
+     * {@link #checkUserActionRequirement(PackageInstallerSession, IntentSender)}.
      */
     private static final int INVALID_TARGET_SDK_VERSION = Integer.MAX_VALUE;
 
@@ -360,6 +368,7 @@
     @GuardedBy("mLock")
     private boolean mShouldBeSealed = false;
 
+    private final AtomicBoolean mPreapprovalRequested = new AtomicBoolean(false);
     private final AtomicBoolean mCommitted = new AtomicBoolean(false);
 
     /**
@@ -387,6 +396,9 @@
     @GuardedBy("mLock")
     private IntentSender mRemoteStatusReceiver;
 
+    @GuardedBy("mLock")
+    private PreapprovalDetails mPreapprovalDetails;
+
     /** Fields derived from commit parsing */
     @GuardedBy("mLock")
     private String mPackageName;
@@ -740,11 +752,12 @@
                     final Bundle extras = (Bundle) args.arg3;
                     final IntentSender statusReceiver = (IntentSender) args.arg4;
                     final int returnCode = args.argi1;
+                    final boolean isPreapproval = args.argi2 == 1;
                     args.recycle();
 
                     sendOnPackageInstalled(mContext, statusReceiver, sessionId,
                             isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
-                            packageName, returnCode, message, extras);
+                            packageName, returnCode, isPreapproval, message, extras);
 
                     break;
                 case MSG_SESSION_VALIDATION_FAILURE:
@@ -752,6 +765,9 @@
                     final String detailMessage = (String) msg.obj;
                     onSessionValidationFailure(error, detailMessage);
                     break;
+                case MSG_PRE_APPROVAL_REQUEST:
+                    handlePreapprovalRequest();
+                    break;
             }
 
             return true;
@@ -779,10 +795,7 @@
      */
     private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwner() {
         assertNotLocked("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
-        // It is safe to access mInstallerUid and mInstallSource without lock
-        // because they are immutable after sealing.
-        assertSealed("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
-        if (userId != UserHandle.getUserId(mInstallerUid)) {
+        if (userId != UserHandle.getUserId(getInstallerUid())) {
             return false;
         }
         DevicePolicyManagerInternal dpmi =
@@ -874,7 +887,7 @@
 
         if (snapshot.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid,
                 userId)) {
-            // show the installer to account for device poslicy or unknown sources use cases
+            // show the installer to account for device policy or unknown sources use cases
             return USER_ACTION_REQUIRED;
         }
 
@@ -1031,18 +1044,22 @@
                     mResolvedBaseFile.getAbsolutePath() : null;
             info.progress = progress;
             info.sealed = mSealed;
-            info.isCommitted = mCommitted.get();
+            info.isCommitted = isCommitted();
+            info.isPreapprovalRequested = isPreapprovalRequested();
             info.active = mActiveCount.get() > 0;
 
             info.mode = params.mode;
             info.installReason = params.installReason;
             info.installScenario = params.installScenario;
             info.sizeBytes = params.sizeBytes;
-            info.appPackageName = mPackageName != null ? mPackageName : params.appPackageName;
+            info.appPackageName = mPreapprovalDetails != null ? mPreapprovalDetails.getPackageName()
+                    : mPackageName != null ? mPackageName : params.appPackageName;
             if (includeIcon) {
-                info.appIcon = params.appIcon;
+                info.appIcon = mPreapprovalDetails != null && mPreapprovalDetails.getIcon() != null
+                        ? mPreapprovalDetails.getIcon() : params.appIcon;
             }
-            info.appLabel = params.appLabel;
+            info.appLabel =
+                    mPreapprovalDetails != null ? mPreapprovalDetails.getLabel() : params.appLabel;
 
             info.installLocation = params.installLocation;
             if (!scrubData) {
@@ -1086,6 +1103,11 @@
         }
     }
 
+    /** @hide */
+    boolean isPreapprovalRequested() {
+        return mPreapprovalRequested.get();
+    }
+
     /** {@hide} */
     boolean isCommitted() {
         return mCommitted.get();
@@ -1122,6 +1144,14 @@
     }
 
     @GuardedBy("mLock")
+    private void assertPreparedAndNotPreapprovalRequestedLocked(String cookie) {
+        assertPreparedAndNotSealedLocked(cookie);
+        if (isPreapprovalRequested()) {
+            throw new IllegalStateException(cookie + " not allowed after requesting");
+        }
+    }
+
+    @GuardedBy("mLock")
     private void assertPreparedAndNotSealedLocked(String cookie) {
         assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
         if (mSealed) {
@@ -1132,7 +1162,7 @@
     @GuardedBy("mLock")
     private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
         assertPreparedAndNotDestroyedLocked(cookie);
-        if (mCommitted.get()) {
+        if (isCommitted()) {
             throw new SecurityException(cookie + " not allowed after commit");
         }
     }
@@ -1173,7 +1203,7 @@
 
     @GuardedBy("mProgressLock")
     private void computeProgressLocked(boolean forcePublish) {
-        if (!isIncrementalInstallation() || !mCommitted.get()) {
+        if (!isIncrementalInstallation() || !isCommitted()) {
             mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
                     + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
         } else {
@@ -1200,7 +1230,7 @@
         assertCallerIsOwnerRootOrVerifier();
         synchronized (mLock) {
             assertPreparedAndNotDestroyedLocked("getNames");
-            if (!mCommitted.get()) {
+            if (!isCommitted()) {
                 return getNamesLocked();
             } else {
                 return getStageDirContentsLocked();
@@ -1640,11 +1670,7 @@
 
     @Override
     public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
-        if (hasParentSessionId()) {
-            throw new IllegalStateException(
-                    "Session " + sessionId + " is a child of multi-package session "
-                            + getParentSessionId() +  " and may not be committed directly.");
-        }
+        assertNotChild("commit");
 
         if (!markAsSealed(statusReceiver, forTransfer)) {
             return;
@@ -1708,6 +1734,20 @@
         }
     }
 
+    @WorkerThread
+    private void handlePreapprovalRequest() {
+        /**
+         * Stops the process if the session needs user action. When the user answers the yes,
+         * {@link #setPermissionsResult(boolean)} is called and then
+         * {@link #MSG_PRE_APPROVAL_REQUEST} is handled to come back here to check again.
+         */
+        if (sendPendingUserActionIntentIfNeeded()) {
+            return;
+        }
+
+        dispatchSessionPreappoved();
+    }
+
     private final class FileSystemConnector extends
             IPackageInstallerSessionFileSystemConnector.Stub {
         final Set<String> mAddedFiles = new ArraySet<>();
@@ -1835,7 +1875,7 @@
         //             single / child sessions.
         try {
             synchronized (mLock) {
-                if (mCommitted.get()) {
+                if (isCommitted()) {
                     return true;
                 }
                 // Read transfers from the original owner stay open, but as the session's data
@@ -2115,7 +2155,11 @@
      */
     @WorkerThread
     private boolean sendPendingUserActionIntentIfNeeded() {
-        assertNotChild("PackageInstallerSession#sendPendingUserActionIntentIfNeeded");
+        // To support pre-approval request of atomic install, we allow child session to handle
+        // the result by itself since it has the status receiver.
+        if (isCommitted()) {
+            assertNotChild("PackageInstallerSession#sendPendingUserActionIntentIfNeeded");
+        }
 
         final IntentSender statusReceiver = getRemoteStatusReceiver();
         return sessionContains(s -> checkUserActionRequirement(s, statusReceiver));
@@ -2420,7 +2464,10 @@
         // User needs to confirm installation;
         // give installer an intent they can use to involve
         // user.
-        final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
+        final boolean isPreapproval = isPreapprovalRequested() && !isCommitted();
+        final Intent intent = new Intent(
+                isPreapproval ? PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL
+                        : PackageInstaller.ACTION_CONFIRM_INSTALL);
         intent.setPackage(mPm.getPackageInstallerPackageName());
         intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
 
@@ -3009,6 +3056,9 @@
                 }
             }
         }
+
+        assertPreapprovalDetailsConsistentIfNeededLocked(packageLite, pkgInfo);
+
         if (packageLite.isUseEmbeddedDex()) {
             for (File file : mResolvedStagedFiles) {
                 if (file.getName().endsWith(".apk")
@@ -3253,6 +3303,78 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void assertPreapprovalDetailsConsistentIfNeededLocked(@NonNull PackageLite packageLite,
+            @Nullable PackageInfo info) throws PackageManagerException {
+        if (mPreapprovalDetails == null || !isPreapprovalRequested()) {
+            return;
+        }
+
+        if (!TextUtils.equals(mPackageName, mPreapprovalDetails.getPackageName())) {
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                    mPreapprovalDetails + " inconsistent with " + mPackageName);
+        }
+
+        // In case the app label in PreapprovalDetails from different locale in split APK,
+        // we check all APK files to find the app label.
+        final PackageInfo packageInfo =
+                info != null ? info : mContext.getPackageManager().getPackageArchiveInfo(
+                        packageLite.getPath(), PackageInfoFlags.of(0));
+        if (packageInfo == null) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Failure to obtain package info.");
+        }
+        final List<String> filePaths = packageLite.getAllApkPaths();
+        final String appLabel = mPreapprovalDetails.getLabel();
+        final ULocale appLocale = mPreapprovalDetails.getLocale();
+        final ApplicationInfo appInfo = packageInfo.applicationInfo;
+        boolean appLabelMatched = false;
+        for (int i = filePaths.size() - 1; i >= 0 && !appLabelMatched; i--) {
+            appLabelMatched |= TextUtils.equals(getAppLabel(filePaths.get(i), appLocale, appInfo),
+                    appLabel);
+        }
+        if (!appLabelMatched) {
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                    mPreapprovalDetails + " inconsistent with app label");
+        }
+    }
+
+    private CharSequence getAppLabel(String path, ULocale locale, ApplicationInfo appInfo)
+            throws PackageManagerException {
+        final Resources pRes = mContext.getResources();
+        final AssetManager assetManager = new AssetManager();
+        final Configuration config = new Configuration(pRes.getConfiguration());
+        final ApkAssets apkAssets;
+        try {
+            apkAssets = ApkAssets.loadFromPath(path);
+        } catch (IOException e) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Failure to get resources from package archive " + path);
+        }
+        assetManager.setApkAssets(new ApkAssets[]{apkAssets}, false /* invalidateCaches */);
+        config.setLocale(locale.toLocale());
+        final Resources res = new Resources(assetManager, pRes.getDisplayMetrics(), config);
+        return tryLoadingAppLabel(res, appInfo);
+    }
+
+    private CharSequence tryLoadingAppLabel(@NonNull Resources res, @NonNull ApplicationInfo info) {
+        CharSequence label = null;
+        // Try to load the label from the package's resources. If an app has not explicitly
+        // specified any label, just use the package name.
+        if (info.labelRes != 0) {
+            try {
+                label = res.getText(info.labelRes);
+            } catch (Resources.NotFoundException ignore) {
+            }
+        }
+        if (label == null) {
+            label = (info.nonLocalizedLabel != null)
+                    ? info.nonLocalizedLabel : info.packageName;
+        }
+
+        return label;
+    }
+
     private SigningDetails unsafeGetCertsWithoutVerification(String path)
             throws PackageManagerException {
         final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
@@ -3469,11 +3591,13 @@
     }
 
     void setPermissionsResult(boolean accepted) {
-        if (!isSealed()) {
+        if (!isSealed() && !isPreapprovalRequested()) {
             throw new SecurityException("Must be sealed to accept permissions");
         }
 
-        PackageInstallerSession root = hasParentSessionId()
+        // To support pre-approval request of atomic install, we allow child session to handle
+        // the result by itself since it has the status receiver.
+        final PackageInstallerSession root = hasParentSessionId() && isCommitted()
                 ? mSessionProvider.getSession(getParentSessionId()) : this;
 
         if (accepted) {
@@ -3481,7 +3605,8 @@
             synchronized (mLock) {
                 mPermissionsManuallyAccepted = true;
             }
-            root.mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
+            root.mHandler.obtainMessage(
+                    isCommitted() ? MSG_INSTALL : MSG_PRE_APPROVAL_REQUEST).sendToTarget();
         } else {
             root.destroy();
             root.dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
@@ -3593,7 +3718,7 @@
             mDestroyed = true;
             r = () -> {
                 assertNotLocked("abandonStaged");
-                if (isStaged() && mCommitted.get()) {
+                if (isStaged() && isCommitted()) {
                     mStagingManager.abortCommittedSession(mStagedSession);
                 }
                 destroy();
@@ -3937,7 +4062,7 @@
     private boolean canBeAddedAsChild(int parentCandidate) {
         synchronized (mLock) {
             return (!hasParentSessionId() || mParentSessionId == parentCandidate)
-                    && !mCommitted.get()
+                    && !isCommitted()
                     && !mDestroyed;
         }
     }
@@ -4096,10 +4221,68 @@
             args.arg3 = extras;
             args.arg4 = statusReceiver;
             args.argi1 = returnCode;
+            args.argi2 = isPreapprovalRequested() && !isCommitted() ? 1 : 0;
             mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
         }
     }
 
+    private void dispatchSessionPreappoved() {
+        final IntentSender target = getRemoteStatusReceiver();
+        final Intent intent = new Intent();
+        intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+        intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
+        intent.putExtra(PackageInstaller.EXTRA_PRE_APPROVAL, true);
+        try {
+            target.sendIntent(mContext, 0 /* code */, intent, null /* onFinished */,
+                    null /* handler */);
+        } catch (IntentSender.SendIntentException ignored) {
+        }
+    }
+
+    @Override
+    public void requestUserPreapproval(@NonNull PreapprovalDetails details,
+            @NonNull IntentSender statusReceiver) {
+        validatePreapprovalRequest(details, statusReceiver);
+        dispatchPreapprovalRequest();
+    }
+
+    /**
+     * Validates whether the necessary information (e.g., PreapprovalDetails) are provided.
+     */
+    private void validatePreapprovalRequest(@NonNull PreapprovalDetails details,
+            @NonNull IntentSender statusReceiver) {
+        assertCallerIsOwnerOrRoot();
+        if (isMultiPackage()) {
+            throw new IllegalStateException(
+                    "Session " + sessionId + " is a parent of multi-package session and "
+                            + "requestUserPreapproval on the parent session isn't supported.");
+        }
+
+        synchronized (mLock) {
+            assertPreparedAndNotSealedLocked("request of session " + sessionId);
+            mPreapprovalDetails = details;
+            setRemoteStatusReceiver(statusReceiver);
+        }
+    }
+
+    private void dispatchPreapprovalRequest() {
+        synchronized (mLock) {
+            assertPreparedAndNotPreapprovalRequestedLocked("dispatchPreapprovalRequest");
+        }
+
+        // Mark this session are pre-approval requested, and ready to progress to the next phase.
+        markAsPreapprovalRequested();
+
+        mHandler.obtainMessage(MSG_PRE_APPROVAL_REQUEST).sendToTarget();
+    }
+
+    /**
+     * Marks this session as pre-approval requested, and prevents further related modification.
+     */
+    private void markAsPreapprovalRequested() {
+        mPreapprovalRequested.set(true);
+    }
+
     void setSessionReady() {
         synchronized (mLock) {
             // Do not allow destroyed/failed session to change state
@@ -4265,6 +4448,7 @@
         pw.printPair("mClientProgress", clientProgress);
         pw.printPair("mProgress", progress);
         pw.printPair("mCommitted", mCommitted);
+        pw.printPair("mPreapprovalRequested", mPreapprovalRequested);
         pw.printPair("mSealed", mSealed);
         pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
         pw.printPair("mStageDirInUse", mStageDirInUse);
@@ -4282,6 +4466,7 @@
         pw.printPair("mSessionReady", mSessionReady);
         pw.printPair("mSessionErrorCode", mSessionErrorCode);
         pw.printPair("mSessionErrorMessage", mSessionErrorMessage);
+        pw.printPair("mPreapprovalDetails", mPreapprovalDetails);
         pw.println();
 
         pw.decreaseIndent();
@@ -4295,6 +4480,8 @@
         final Intent fillIn = new Intent();
         fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
+        fillIn.putExtra(PackageInstaller.EXTRA_PRE_APPROVAL,
+                PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL.equals(intent.getAction()));
         fillIn.putExtra(Intent.EXTRA_INTENT, intent);
         try {
             target.sendIntent(context, 0, fillIn, null, null);
@@ -4307,7 +4494,7 @@
      */
     private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
             boolean showNotification, int userId, String basePackageName, int returnCode,
-            String msg, Bundle extras) {
+            boolean isPreapproval, String msg, Bundle extras) {
         if (INSTALL_SUCCEEDED == returnCode && showNotification) {
             boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
             Notification notification = PackageInstallerService.buildSuccessNotification(context,
@@ -4330,6 +4517,7 @@
         fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
                 PackageManager.installStatusToString(returnCode, msg));
         fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
+        fillIn.putExtra(PackageInstaller.EXTRA_PRE_APPROVAL, isPreapproval);
         if (extras != null) {
             final String existing = extras.getString(
                     PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
@@ -4448,7 +4636,7 @@
                 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
             }
             writeBooleanAttribute(out, ATTR_PREPARED, mPrepared);
-            writeBooleanAttribute(out, ATTR_COMMITTED, mCommitted.get());
+            writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
             writeBooleanAttribute(out, ATTR_DESTROYED, mDestroyed);
             writeBooleanAttribute(out, ATTR_SEALED, mSealed);
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d939ca6..8fed153 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6031,7 +6031,7 @@
             new DumpHelper(mPermissionManager, mStorageEventHelper,
                     mDomainVerificationManager, mInstallerService, mRequiredVerifierPackages,
                     knownPackages, mChangedPackagesTracker, availableFeatures, protectedBroadcasts,
-                    getPerUidReadTimeouts(snapshot)
+                    getPerUidReadTimeouts(snapshot), mSnapshotStatistics
             ).doDump(snapshot, fd, pw, args);
         }
     }
diff --git a/services/core/java/com/android/server/pm/SnapshotStatistics.java b/services/core/java/com/android/server/pm/SnapshotStatistics.java
index 95f80de..2cfc894 100644
--- a/services/core/java/com/android/server/pm/SnapshotStatistics.java
+++ b/services/core/java/com/android/server/pm/SnapshotStatistics.java
@@ -240,11 +240,6 @@
         public int mTotalUsed = 0;
 
         /**
-         * The total number of times a snapshot was bypassed because corking was in effect.
-         */
-        public int mTotalCorked = 0;
-
-        /**
          * The total number of builds that count as big, which means they took longer than
          * SNAPSHOT_BIG_BUILD_TIME_NS.
          */
@@ -297,13 +292,6 @@
             }
         }
 
-        /**
-         * Record a cork.
-         */
-        private void corked() {
-            mTotalCorked++;
-        }
-
         private Stats(long now) {
             mStartTimeUs = now;
             mTimes = new int[mTimeBins.count()];
@@ -321,7 +309,6 @@
             mUsed = Arrays.copyOf(orig.mUsed, orig.mUsed.length);
             mTotalBuilds = orig.mTotalBuilds;
             mTotalUsed = orig.mTotalUsed;
-            mTotalCorked = orig.mTotalCorked;
             mBigBuilds = orig.mBigBuilds;
             mShortLived = orig.mShortLived;
             mTotalTimeUs = orig.mTotalTimeUs;
@@ -379,7 +366,6 @@
          * Dump the summary statistics record.  Choose the header or the data.
          *    number of builds
          *    number of uses
-         *    number of corks
          *    number of big builds
          *    number of short lifetimes
          *    cumulative build time, in seconds
@@ -388,13 +374,13 @@
         private void dumpStats(PrintWriter pw, String indent, long now, boolean header) {
             dumpPrefix(pw, indent, now, header, "Summary stats");
             if (header) {
-                pw.format(Locale.US, "  %10s  %10s  %10s  %10s  %10s  %10s  %10s",
-                          "TotBlds", "TotUsed", "TotCork", "BigBlds", "ShortLvd",
+                pw.format(Locale.US, "  %10s  %10s  %10s  %10s  %10s  %10s",
+                          "TotBlds", "TotUsed", "BigBlds", "ShortLvd",
                           "TotTime", "MaxTime");
             } else {
                 pw.format(Locale.US,
-                        "  %10d  %10d  %10d  %10d  %10d  %10d  %10d",
-                        mTotalBuilds, mTotalUsed, mTotalCorked, mBigBuilds, mShortLived,
+                        "  %10d  %10d  %10d  %10d  %10d  %10d",
+                        mTotalBuilds, mTotalUsed, mBigBuilds, mShortLived,
                         mTotalTimeUs / 1000, mMaxBuildTimeUs / 1000);
             }
             pw.println();
@@ -559,16 +545,6 @@
     }
 
     /**
-     * Record a corked snapshot request.
-     */
-    public final void corked() {
-        synchronized (mLock) {
-            mShort[0].corked();
-            mLong[0].corked();
-        }
-    }
-
-    /**
      * Roll a stats array.  Shift the elements up an index and create a new element at
      * index zero.  The old element zero is completed with the specified time.
      */
@@ -624,8 +600,7 @@
      * Dump the statistics.  The format is compatible with the PackageManager dumpsys
      * output.
      */
-    public void dump(PrintWriter pw, String indent, long now, int unrecorded,
-                     int corkLevel, boolean brief) {
+    public void dump(PrintWriter pw, String indent, long now, int unrecorded, boolean brief) {
         // Grab the raw statistics under lock, but print them outside of the lock.
         Stats[] l;
         Stats[] s;
@@ -635,8 +610,7 @@
             s = Arrays.copyOf(mShort, mShort.length);
             s[0] = new Stats(s[0]);
         }
-        pw.format(Locale.US, "%s Unrecorded-hits: %d  Cork-level: %d", indent,
-                  unrecorded, corkLevel);
+        pw.format(Locale.US, "%s Unrecorded-hits: %d", indent, unrecorded);
         pw.println();
         dump(pw, indent, now, l, s, "stats");
         if (brief) {
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
new file mode 100644
index 0000000..5f76fbc
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2022 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.power.stats;
+
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.LongSparseArray;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+import android.util.TimeSparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IntPair;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Stores stats about CPU wakeups and tries to attribute them to subsystems and uids.
+ */
+public class CpuWakeupStats {
+    private static final String SUBSYSTEM_ALARM_STRING = "Alarm";
+    @VisibleForTesting
+    static final long WAKEUP_RETENTION_MS = 3 * 24 * 60 * 60_000; // 3 days.
+    @VisibleForTesting
+    static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
+
+    private final IrqDeviceMap mIrqDeviceMap;
+    private final WakingActivityHistory mRecentWakingActivity = new WakingActivityHistory();
+
+    @VisibleForTesting
+    final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>();
+    @VisibleForTesting
+    final TimeSparseArray<SparseArray<SparseBooleanArray>> mWakeupAttribution =
+            new TimeSparseArray<>();
+
+    public CpuWakeupStats(Context context, int mapRes) {
+        mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes);
+    }
+
+    /** Notes a wakeup reason as reported by SuspendControlService to battery stats. */
+    public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime,
+            String rawReason) {
+        final Wakeup parsedWakeup = new Wakeup(rawReason, elapsedRealtime, uptime);
+        mWakeupEvents.put(elapsedRealtime, parsedWakeup);
+        attemptAttributionFor(parsedWakeup);
+        // Assuming that wakeups always arrive in monotonically increasing elapsedRealtime order,
+        // we can delete all history that will not be useful in attributing future wakeups.
+        mRecentWakingActivity.clearAllBefore(elapsedRealtime - WAKEUP_REASON_HALF_WINDOW_MS);
+
+        // Limit history of wakeups and their attribution to the last WAKEUP_RETENTION_MS. Note that
+        // the last wakeup and its attribution (if computed) is always stored, even if that wakeup
+        // had occurred before WAKEUP_RETENTION_MS.
+        int lastIdx = mWakeupEvents.closestIndexOnOrBefore(elapsedRealtime - WAKEUP_RETENTION_MS);
+        for (int i = lastIdx; i >= 0; i--) {
+            mWakeupEvents.removeAt(i);
+        }
+        lastIdx = mWakeupAttribution.closestIndexOnOrBefore(elapsedRealtime - WAKEUP_RETENTION_MS);
+        for (int i = lastIdx; i >= 0; i--) {
+            mWakeupAttribution.removeAt(i);
+        }
+    }
+
+    /** Notes a waking activity that could have potentially woken up the CPU. */
+    public synchronized void noteWakingActivity(int subsystem, long elapsedRealtime, int... uids) {
+        if (!attemptAttributionWith(subsystem, elapsedRealtime, uids)) {
+            mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime, uids);
+        }
+    }
+
+    private synchronized void attemptAttributionFor(Wakeup wakeup) {
+        final SparseBooleanArray subsystems = getResponsibleSubsystemsForWakeup(wakeup);
+        if (subsystems == null) {
+            // We don't support attribution for this kind of wakeup yet.
+            return;
+        }
+
+        SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
+        if (attribution == null) {
+            attribution = new SparseArray<>();
+            mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
+        }
+
+        for (int subsystemIdx = 0; subsystemIdx < subsystems.size(); subsystemIdx++) {
+            final int subsystem = subsystems.keyAt(subsystemIdx);
+
+            // Blame all activity that happened WAKEUP_REASON_HALF_WINDOW_MS before or after
+            // the wakeup from each responsible subsystem.
+            final long startTime = wakeup.mElapsedMillis - WAKEUP_REASON_HALF_WINDOW_MS;
+            final long endTime = wakeup.mElapsedMillis + WAKEUP_REASON_HALF_WINDOW_MS;
+
+            final SparseBooleanArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
+                    startTime, endTime);
+            attribution.put(subsystem, uidsToBlame);
+        }
+    }
+
+    private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed,
+            int... uids) {
+        final int startIdx = mWakeupEvents.closestIndexOnOrAfter(
+                activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS);
+        final int endIdx = mWakeupEvents.closestIndexOnOrBefore(
+                activityElapsed + WAKEUP_REASON_HALF_WINDOW_MS);
+
+        for (int wakeupIdx = startIdx; wakeupIdx <= endIdx; wakeupIdx++) {
+            final Wakeup wakeup = mWakeupEvents.valueAt(wakeupIdx);
+            final SparseBooleanArray subsystems = getResponsibleSubsystemsForWakeup(wakeup);
+            if (subsystems == null) {
+                // Unsupported for attribution
+                continue;
+            }
+            if (subsystems.get(subsystem)) {
+                // We don't expect more than one wakeup to be found within such a short window, so
+                // just attribute this one and exit
+                SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+                        wakeup.mElapsedMillis);
+                if (attribution == null) {
+                    attribution = new SparseArray<>();
+                    mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
+                }
+                SparseBooleanArray uidsToBlame = attribution.get(subsystem);
+                if (uidsToBlame == null) {
+                    uidsToBlame = new SparseBooleanArray(uids.length);
+                    attribution.put(subsystem, uidsToBlame);
+                }
+                for (final int uid : uids) {
+                    uidsToBlame.put(uid, true);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Dumps the relevant stats for cpu wakeups and their attribution to subsystem and uids */
+    public synchronized void dump(IndentingPrintWriter pw, long nowElapsed) {
+        pw.println("CPU wakeup stats:");
+        pw.increaseIndent();
+
+        mIrqDeviceMap.dump(pw);
+        pw.println();
+
+        mRecentWakingActivity.dump(pw, nowElapsed);
+        pw.println();
+
+        final SparseLongArray attributionStats = new SparseLongArray();
+        pw.println("Wakeup events:");
+        pw.increaseIndent();
+        for (int i = mWakeupEvents.size() - 1; i >= 0; i--) {
+            TimeUtils.formatDuration(mWakeupEvents.keyAt(i), nowElapsed, pw);
+            pw.println(":");
+
+            pw.increaseIndent();
+            final Wakeup wakeup = mWakeupEvents.valueAt(i);
+            pw.println(wakeup);
+            pw.print("Attribution: ");
+            final SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+                    wakeup.mElapsedMillis);
+            if (attribution == null) {
+                pw.println("N/A");
+            } else {
+                for (int subsystemIdx = 0; subsystemIdx < attribution.size(); subsystemIdx++) {
+                    if (subsystemIdx > 0) {
+                        pw.print(", ");
+                    }
+                    final long counters = attributionStats.get(attribution.keyAt(subsystemIdx),
+                            IntPair.of(0, 0));
+                    int attributed = IntPair.first(counters);
+                    final int total = IntPair.second(counters) + 1;
+
+                    pw.print("subsystem: " + subsystemToString(attribution.keyAt(subsystemIdx)));
+                    pw.print(", uids: [");
+                    final SparseBooleanArray uids = attribution.valueAt(subsystemIdx);
+                    if (uids != null) {
+                        for (int uidIdx = 0; uidIdx < uids.size(); uidIdx++) {
+                            if (uidIdx > 0) {
+                                pw.print(", ");
+                            }
+                            UserHandle.formatUid(pw, uids.keyAt(uidIdx));
+                        }
+                        attributed++;
+                    }
+                    pw.print("]");
+
+                    attributionStats.put(attribution.keyAt(subsystemIdx),
+                            IntPair.of(attributed, total));
+                }
+                pw.println();
+            }
+            pw.decreaseIndent();
+        }
+        pw.decreaseIndent();
+
+        pw.println("Attribution stats:");
+        pw.increaseIndent();
+        for (int i = 0; i < attributionStats.size(); i++) {
+            pw.print("Subsystem " + subsystemToString(attributionStats.keyAt(i)));
+            pw.print(": ");
+            final long ratio = attributionStats.valueAt(i);
+            pw.println(IntPair.first(ratio) + "/" + IntPair.second(ratio));
+        }
+        pw.println("Total: " + mWakeupEvents.size());
+        pw.decreaseIndent();
+
+        pw.decreaseIndent();
+        pw.println();
+    }
+
+    private static final class WakingActivityHistory {
+        private static final long WAKING_ACTIVITY_RETENTION_MS = 3 * 60 * 60_000; // 3 hours.
+        private SparseArray<TimeSparseArray<SparseBooleanArray>> mWakingActivity =
+                new SparseArray<>();
+
+        void recordActivity(int subsystem, long elapsedRealtime, int... uids) {
+            if (uids == null) {
+                return;
+            }
+            TimeSparseArray<SparseBooleanArray> wakingActivity = mWakingActivity.get(subsystem);
+            if (wakingActivity == null) {
+                wakingActivity = new TimeSparseArray<>();
+                mWakingActivity.put(subsystem, wakingActivity);
+            }
+            SparseBooleanArray uidsToBlame = wakingActivity.get(elapsedRealtime);
+            if (uidsToBlame == null) {
+                uidsToBlame = new SparseBooleanArray(uids.length);
+                wakingActivity.put(elapsedRealtime, uidsToBlame);
+            }
+            for (int i = 0; i < uids.length; i++) {
+                uidsToBlame.put(uids[i], true);
+            }
+            // Limit activity history per subsystem to the last WAKING_ACTIVITY_RETENTION_MS.
+            // Note that the last activity is always present, even if it occurred before
+            // WAKING_ACTIVITY_RETENTION_MS.
+            final int endIdx = wakingActivity.closestIndexOnOrBefore(
+                    elapsedRealtime - WAKING_ACTIVITY_RETENTION_MS);
+            for (int i = endIdx; i >= 0; i--) {
+                wakingActivity.removeAt(endIdx);
+            }
+        }
+
+        void clearAllBefore(long elapsedRealtime) {
+            for (int subsystemIdx = mWakingActivity.size() - 1; subsystemIdx >= 0; subsystemIdx--) {
+                final TimeSparseArray<SparseBooleanArray> activityPerSubsystem =
+                        mWakingActivity.valueAt(subsystemIdx);
+                final int endIdx = activityPerSubsystem.closestIndexOnOrBefore(elapsedRealtime);
+                for (int removeIdx = endIdx; removeIdx >= 0; removeIdx--) {
+                    activityPerSubsystem.removeAt(removeIdx);
+                }
+                // Generally waking activity is a high frequency occurrence for any subsystem, so we
+                // don't delete the TimeSparseArray even if it is now empty, to avoid object churn.
+                // This will leave one TimeSparseArray per subsystem, which are few right now.
+            }
+        }
+
+        SparseBooleanArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
+            final SparseBooleanArray uidsToReturn = new SparseBooleanArray();
+
+            final TimeSparseArray<SparseBooleanArray> activityForSubsystem =
+                    mWakingActivity.get(subsystem);
+            if (activityForSubsystem != null) {
+                final int startIdx = activityForSubsystem.closestIndexOnOrAfter(startElapsed);
+                final int endIdx = activityForSubsystem.closestIndexOnOrBefore(endElapsed);
+                for (int i = endIdx; i >= startIdx; i--) {
+                    final SparseBooleanArray uidsForTime = activityForSubsystem.valueAt(i);
+                    for (int j = 0; j < uidsForTime.size(); j++) {
+                        if (uidsForTime.valueAt(j)) {
+                            uidsToReturn.put(uidsForTime.keyAt(j), true);
+                        }
+                    }
+                }
+                // More efficient to remove in a separate loop as it avoids repeatedly calling gc().
+                for (int i = endIdx; i >= startIdx; i--) {
+                    activityForSubsystem.removeAt(i);
+                }
+                // Generally waking activity is a high frequency occurrence for any subsystem, so we
+                // don't delete the TimeSparseArray even if it is now empty, to avoid object churn.
+                // This will leave one TimeSparseArray per subsystem, which are few right now.
+            }
+            return uidsToReturn.size() > 0 ? uidsToReturn : null;
+        }
+
+        void dump(IndentingPrintWriter pw, long nowElapsed) {
+            pw.println("Recent waking activity:");
+            pw.increaseIndent();
+            for (int i = 0; i < mWakingActivity.size(); i++) {
+                pw.println("Subsystem " + subsystemToString(mWakingActivity.keyAt(i)) + ":");
+                final LongSparseArray<SparseBooleanArray> wakingActivity =
+                        mWakingActivity.valueAt(i);
+                if (wakingActivity == null) {
+                    continue;
+                }
+                pw.increaseIndent();
+                for (int j = wakingActivity.size() - 1; j >= 0; j--) {
+                    TimeUtils.formatDuration(wakingActivity.keyAt(j), nowElapsed, pw);
+                    final SparseBooleanArray uidsToBlame = wakingActivity.valueAt(j);
+                    if (uidsToBlame == null) {
+                        pw.println();
+                        continue;
+                    }
+                    pw.print(": ");
+                    for (int k = 0; k < uidsToBlame.size(); k++) {
+                        if (uidsToBlame.valueAt(k)) {
+                            UserHandle.formatUid(pw, uidsToBlame.keyAt(k));
+                            pw.print(", ");
+                        }
+                    }
+                    pw.println();
+                }
+                pw.decreaseIndent();
+            }
+            pw.decreaseIndent();
+        }
+    }
+
+    private SparseBooleanArray getResponsibleSubsystemsForWakeup(Wakeup wakeup) {
+        if (ArrayUtils.isEmpty(wakeup.mDevices)) {
+            return null;
+        }
+        final SparseBooleanArray result = new SparseBooleanArray();
+        for (final Wakeup.IrqDevice device : wakeup.mDevices) {
+            final List<String> rawSubsystems = mIrqDeviceMap.getSubsystemsForDevice(device.mDevice);
+
+            boolean anyKnownSubsystem = false;
+            if (rawSubsystems != null) {
+                for (int i = 0; i < rawSubsystems.size(); i++) {
+                    final int subsystem = stringToKnownSubsystem(rawSubsystems.get(i));
+                    if (subsystem != CPU_WAKEUP_SUBSYSTEM_UNKNOWN) {
+                        // Just in case the xml had arbitrary subsystem names, we want to make sure
+                        // that we only put the known ones into our attribution map.
+                        result.put(subsystem, true);
+                        anyKnownSubsystem = true;
+                    }
+                }
+            }
+            if (!anyKnownSubsystem) {
+                result.put(CPU_WAKEUP_SUBSYSTEM_UNKNOWN, true);
+            }
+        }
+        return result;
+    }
+
+    static int stringToKnownSubsystem(String rawSubsystem) {
+        switch (rawSubsystem) {
+            case SUBSYSTEM_ALARM_STRING:
+                return CPU_WAKEUP_SUBSYSTEM_ALARM;
+        }
+        return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
+    }
+
+    static String subsystemToString(int subsystem) {
+        switch (subsystem) {
+            case CPU_WAKEUP_SUBSYSTEM_ALARM:
+                return SUBSYSTEM_ALARM_STRING;
+            case CPU_WAKEUP_SUBSYSTEM_UNKNOWN:
+                return "Unknown";
+        }
+        return "N/A";
+    }
+
+    private static final class Wakeup {
+        private static final String PARSER_TAG = "CpuWakeupStats.Wakeup";
+        private static final String ABORT_REASON_PREFIX = "Abort";
+        private static final Pattern sIrqPattern = Pattern.compile("(\\d+)\\s+(\\S+)");
+
+        String mRawReason;
+        long mElapsedMillis;
+        long mUptimeMillis;
+        IrqDevice[] mDevices;
+
+        Wakeup(String rawReason, long elapsedMillis, long uptimeMillis) {
+            mRawReason = rawReason;
+            mElapsedMillis = elapsedMillis;
+            mUptimeMillis = uptimeMillis;
+            mDevices = parseIrqDevices(rawReason);
+        }
+
+        private static IrqDevice[] parseIrqDevices(String rawReason) {
+            final String[] components = rawReason.split(":");
+            if (ArrayUtils.isEmpty(components) || components[0].startsWith(ABORT_REASON_PREFIX)) {
+                // We don't support parsing aborts yet.
+                return null;
+            }
+
+            int parsedDeviceCount = 0;
+            IrqDevice[] parsedDevices = new IrqDevice[components.length];
+
+            for (String component : components) {
+                final Matcher matcher = sIrqPattern.matcher(component);
+                if (matcher.find()) {
+                    final int line;
+                    final String device;
+                    try {
+                        line = Integer.parseInt(matcher.group(1));
+                        device = matcher.group(2);
+                    } catch (NumberFormatException e) {
+                        Slog.e(PARSER_TAG,
+                                "Exception while parsing device names from part: " + component, e);
+                        continue;
+                    }
+                    parsedDevices[parsedDeviceCount++] = new IrqDevice(line, device);
+                }
+            }
+            return (parsedDeviceCount > 0) ? Arrays.copyOf(parsedDevices, parsedDeviceCount) : null;
+        }
+
+        @Override
+        public String toString() {
+            return "Wakeup{"
+                    + "mRawReason='" + mRawReason + '\''
+                    + ", mElapsedMillis=" + mElapsedMillis
+                    + ", mUptimeMillis=" + TimeUtils.formatDuration(mUptimeMillis)
+                    + ", mDevices=" + Arrays.toString(mDevices)
+                    + '}';
+        }
+
+        static final class IrqDevice {
+            int mLine;
+            String mDevice;
+
+            IrqDevice(int line, String device) {
+                mLine = line;
+                mDevice = device;
+            }
+
+            @Override
+            public String toString() {
+                return "IrqDevice{" + "mLine=" + mLine + ", mDevice='" + mDevice + '\'' + '}';
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/IrqDeviceMap.java b/services/core/java/com/android/server/power/stats/IrqDeviceMap.java
new file mode 100644
index 0000000..091d18e
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/IrqDeviceMap.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 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.power.stats;
+
+import android.annotation.XmlRes;
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.LongSparseArray;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Parses irq_device_map.xml to store a mapping of devices that can send IRQs to the CPU to
+ * subsystems that represent some logical work happening on the device that could need an IRQ.
+ */
+public class IrqDeviceMap {
+    private static final String TAG_IRQ_DEVICE_MAP = "irq-device-map";
+    private static final String TAG_DEVICE = "device";
+    private static final String TAG_SUBSYSTEM = "subsystem";
+    private static final String ATTR_NAME = "name";
+
+    private static LongSparseArray<IrqDeviceMap> sInstanceMap = new LongSparseArray<>(1);
+
+    private final ArrayMap<String, List<String>> mSubsystemsForDevice = new ArrayMap();
+
+    private IrqDeviceMap(XmlResourceParser parser) {
+        try {
+            XmlUtils.beginDocument(parser, TAG_IRQ_DEVICE_MAP);
+
+            int type;
+            String currentDevice = null;
+            final ArraySet<String> subsystems = new ArraySet<>();
+
+            while ((type = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
+                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_DEVICE)) {
+                    currentDevice = parser.getAttributeValue(null, ATTR_NAME);
+                }
+                if (currentDevice != null && type == XmlPullParser.END_TAG
+                        && parser.getName().equals(TAG_DEVICE)) {
+                    final int n = subsystems.size();
+                    if (n > 0) {
+                        mSubsystemsForDevice.put(currentDevice,
+                                Collections.unmodifiableList(new ArrayList<>(subsystems)));
+                    }
+                    subsystems.clear();
+                    currentDevice = null;
+                }
+                if (currentDevice != null && type == XmlPullParser.START_TAG
+                        && parser.getName().equals(TAG_SUBSYSTEM)) {
+                    parser.next();
+                    if (parser.getEventType() == XmlPullParser.TEXT) {
+                        subsystems.add(parser.getText());
+                    }
+                }
+                parser.next();
+            }
+        } catch (XmlPullParserException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            parser.close();
+        }
+    }
+
+    /**
+     * Returns an instance of IrqDeviceMap initialzed with the given context and xml resource.
+     * The xml resource should describe the mapping in a way similar to
+     * core/res/res/xml/irq_device_map.xml.
+     */
+    public static IrqDeviceMap getInstance(Context context, @XmlRes int resId) {
+        synchronized (IrqDeviceMap.class) {
+            final int idx = sInstanceMap.indexOfKey(resId);
+            if (idx >= 0) {
+                return sInstanceMap.valueAt(idx);
+            }
+        }
+        final XmlResourceParser parser = context.getResources().getXml(resId);
+        final IrqDeviceMap irqDeviceMap = new IrqDeviceMap(parser);
+        synchronized (IrqDeviceMap.class) {
+            sInstanceMap.put(resId, irqDeviceMap);
+        }
+        return irqDeviceMap;
+    }
+
+    List<String> getSubsystemsForDevice(String device) {
+        return mSubsystemsForDevice.get(device);
+    }
+
+    void dump(IndentingPrintWriter pw) {
+        pw.println("Irq device map:");
+        pw.increaseIndent();
+
+        final LongSparseArray<IrqDeviceMap> instanceMap;
+        synchronized (IrqDeviceMap.class) {
+            instanceMap = sInstanceMap;
+        }
+        final int idx = instanceMap.indexOfValue(this);
+        final String res = (idx >= 0) ? ("0x" + Long.toHexString(instanceMap.keyAt(idx))) : null;
+        pw.println("Loaded from xml resource: " + res);
+
+        pw.println("Map:");
+        pw.increaseIndent();
+        for (int i = 0; i < mSubsystemsForDevice.size(); i++) {
+            pw.print(mSubsystemsForDevice.keyAt(i) + ": ");
+            pw.println(mSubsystemsForDevice.valueAt(i));
+        }
+        pw.decreaseIndent();
+
+        pw.decreaseIndent();
+    }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
index fd6ec06..f744d00 100644
--- a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
+++ b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
@@ -46,6 +46,8 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
 
 class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener,
         SensorEventListener {
@@ -275,7 +277,7 @@
     public void onSensorChanged(SensorEvent event) {
         // Using log space to represent human sensation (Fechner's Law) instead of lux
         // because lux values causes bright flashes to skew the average very high.
-        addElement(event.timestamp, Math.max(0,
+        addElement(TimeUnit.NANOSECONDS.toMillis(event.timestamp), Math.max(0,
                 (int) (Math.log(event.values[0]) * LIGHT_VALUE_MULTIPLIER)));
         updateLightSession();
         mHandler.removeCallbacksAndMessages(mDelayedUpdateToken);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 690dd10..4111446 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -49,6 +49,31 @@
     void dismissKeyboardShortcutsMenu();
     void toggleKeyboardShortcutsMenu(int deviceId);
 
+    /**
+     * Used by InputMethodManagerService to notify the IME status.
+     *
+     * @param displayId The display to which the IME is bound to.
+     * @param token The IME token.
+     * @param vis Bit flags about the IME visibility.
+     *            (e.g. {@link android.inputmethodservice.InputMethodService#IME_ACTIVE})
+     * @param backDisposition Bit flags about the IME back disposition.
+     *         (e.g. {@link android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT})
+     * @param showImeSwitcher {@code true} when the IME switcher button should be shown.
+     */
+    void setImeWindowStatus(int displayId, IBinder token, int vis,
+            int backDisposition, boolean showImeSwitcher);
+
+    /**
+     * See {@link android.app.StatusBarManager#setIcon(String, int, int, String)}.
+     */
+    void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
+            String contentDescription);
+
+    /**
+     * See {@link android.app.StatusBarManager#setIconVisibility(String, boolean)}.
+     */
+    void setIconVisibility(String slot, boolean visibility);
+
     void showChargingAnimation(int batteryLevel);
 
     /**
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 653b51a9..7ccf85f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -484,6 +484,25 @@
         }
 
         @Override
+        public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+                boolean showImeSwitcher) {
+            StatusBarManagerService.this.setImeWindowStatus(displayId, token, vis, backDisposition,
+                    showImeSwitcher);
+        }
+
+        @Override
+        public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
+                String contentDescription) {
+            StatusBarManagerService.this.setIcon(slot, iconPackage, iconId, iconLevel,
+                    contentDescription);
+        }
+
+        @Override
+        public void setIconVisibility(String slot, boolean visibility) {
+            StatusBarManagerService.this.setIconVisibility(slot, visibility);
+        }
+
+        @Override
         public void showChargingAnimation(int batteryLevel) {
             if (mBar != null) {
                 try {
diff --git a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
index 4803011..b96af89 100644
--- a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
@@ -172,9 +172,14 @@
         mContext.enforceCallingPermission(
                 android.Manifest.permission.SET_TIME, "clear latest network time");
 
-        mTime.clearCachedTimeResult();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mTime.clearCachedTimeResult();
 
-        mLocalLog.log("clearTimeForTests");
+            mLocalLog.log("clearTimeForTests");
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
@@ -188,15 +193,19 @@
         mContext.enforceCallingPermission(
                 android.Manifest.permission.SET_TIME, "force network time refresh");
 
-        boolean success = mTime.forceRefresh();
-        mLocalLog.log("forceRefreshForTests: success=" + success);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            boolean success = mTime.forceRefresh();
+            mLocalLog.log("forceRefreshForTests: success=" + success);
 
-        if (success) {
-            makeNetworkTimeSuggestion(mTime.getCachedTimeResult(),
-                    "Origin: NetworkTimeUpdateService: forceRefreshForTests");
+            if (success) {
+                makeNetworkTimeSuggestion(mTime.getCachedTimeResult(),
+                        "Origin: NetworkTimeUpdateService: forceRefreshForTests");
+            }
+            return success;
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
-
-        return success;
     }
 
     /**
@@ -207,8 +216,13 @@
         mContext.enforceCallingPermission(
                 android.Manifest.permission.SET_TIME, "set NTP server config for tests");
 
-        mLocalLog.log("Setting server config for tests: ntpConnectionInfo=" + ntpConfig);
-        mTime.setServerConfigForTests(ntpConfig);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mLocalLog.log("Setting server config for tests: ntpConnectionInfo=" + ntpConfig);
+            mTime.setServerConfigForTests(ntpConfig);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private void onPollNetworkTime(int event) {
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index c524bc4..f52f0b7 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -1047,6 +1047,29 @@
         }
 
         @Override
+        public void notifyRecordingStarted(IBinder sessionToken, String recordingId, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyRecordingStarted");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyRecordingStarted(recordingId);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyRecordingStarted", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+
+        }
+
+        @Override
         public void startInteractiveApp(IBinder sessionToken, int userId) {
             if (DEBUG) {
                 Slogf.d(TAG, "BinderService#start(userId=%d)", userId);
@@ -2213,6 +2236,23 @@
         }
 
         @Override
+        public void onRequestStartRecording(Uri programUri) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onRequestStartRecording: " + programUri);
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onRequestStartRecording(programUri, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onRequestStartRecording", e);
+                }
+            }
+        }
+
+        @Override
         public void onRequestSigning(String id, String algorithm, String alias, byte[] data) {
             synchronized (mLock) {
                 if (DEBUG) {
diff --git a/services/core/java/com/android/server/utils/EventLogger.java b/services/core/java/com/android/server/utils/EventLogger.java
index 45b4ff8..aa8b94a7 100644
--- a/services/core/java/com/android/server/utils/EventLogger.java
+++ b/services/core/java/com/android/server/utils/EventLogger.java
@@ -23,8 +23,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
 import java.util.Date;
-import java.util.LinkedList;
 import java.util.Locale;
 
 /**
@@ -36,7 +36,7 @@
     private final String mTag;
 
     /** Stores the events using a ring buffer. */
-    private final LinkedList<Event> mEvents;
+    private final ArrayDeque<Event> mEvents;
 
     /**
      * The maximum number of events to keep in {@code mEvents}.
@@ -52,16 +52,18 @@
      * @param tag the string displayed before the recorded log
      */
     public EventLogger(int size, String tag) {
-        mEvents = new LinkedList<Event>();
+        mEvents = new ArrayDeque<>(size);
         mMemSize = size;
         mTag = tag;
     }
 
-    public synchronized void log(Event evt) {
+    /** Enqueues {@code event} to be logged. */
+    public synchronized void log(Event event) {
         if (mEvents.size() >= mMemSize) {
-            mEvents.removeFirst();
+            mEvents.removeLast();
         }
-        mEvents.add(evt);
+
+        mEvents.addFirst(event);
     }
 
     /**
@@ -85,8 +87,10 @@
         log(event.printLog(logType, tag));
     }
 
+    /** Dumps events using {@link PrintWriter} */
     public synchronized void dump(PrintWriter pw) {
         pw.println("Events log: " + mTag);
+
         for (Event evt : mEvents) {
             pw.println(evt.toString());
         }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ce7794d..78b657b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -256,6 +256,7 @@
 import android.app.WaitResult;
 import android.app.WindowConfiguration;
 import android.app.admin.DevicePolicyManager;
+import android.app.assist.ActivityId;
 import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
@@ -5585,7 +5586,9 @@
                         LocalServices.getService(ContentCaptureManagerInternal.class);
                 if (contentCaptureService != null) {
                     contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent,
-                            ActivityEvent.TYPE_ACTIVITY_STARTED);
+                            ActivityEvent.TYPE_ACTIVITY_STARTED,
+                            new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID,
+                                    shareableActivityToken));
                 }
                 break;
             case PAUSED:
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7d23cca..416d546 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -152,6 +152,7 @@
 import android.app.ProfilerInfo;
 import android.app.WaitResult;
 import android.app.admin.DevicePolicyCache;
+import android.app.assist.ActivityId;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.compat.CompatChanges;
@@ -4859,17 +4860,19 @@
 
     void updateActivityUsageStats(ActivityRecord activity, int event) {
         ComponentName taskRoot = null;
+        int taskId = INVALID_TASK_ID;
         final Task task = activity.getTask();
         if (task != null) {
             final ActivityRecord rootActivity = task.getRootActivity();
             if (rootActivity != null) {
                 taskRoot = rootActivity.mActivityComponent;
             }
+            taskId = task.mTaskId;
         }
-
         final Message m = PooledLambda.obtainMessage(
                 ActivityManagerInternal::updateActivityUsageStats, mAmInternal,
-                activity.mActivityComponent, activity.mUserId, event, activity.token, taskRoot);
+                activity.mActivityComponent, activity.mUserId, event, activity.token, taskRoot,
+                new ActivityId(taskId, activity.shareableActivityToken));
         mH.sendMessage(m);
     }
 
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
new file mode 100644
index 0000000..5e44d6c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 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.wm;
+
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
+
+/**
+ * The class that defines default launch params for tasks in desktop mode
+ */
+public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
+
+    private static final String TAG =
+            TAG_WITH_CLASS_NAME ? "DesktopModeLaunchParamsModifier" : TAG_ATM;
+    private static final boolean DEBUG = false;
+
+    // Desktop mode feature flag.
+    static final boolean DESKTOP_MODE_SUPPORTED = SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_mode", false);
+    // Override default freeform task width when desktop mode is enabled. In dips.
+    private static final int DESKTOP_MODE_DEFAULT_WIDTH_DP = SystemProperties.getInt(
+            "persist.wm.debug.desktop_mode.default_width", 840);
+    // Override default freeform task height when desktop mode is enabled. In dips.
+    private static final int DESKTOP_MODE_DEFAULT_HEIGHT_DP = SystemProperties.getInt(
+            "persist.wm.debug.desktop_mode.default_height", 630);
+
+    private StringBuilder mLogBuilder;
+
+    @Override
+    public int onCalculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
+            @Nullable ActivityRecord activity, @Nullable ActivityRecord source,
+            @Nullable ActivityOptions options, @Nullable ActivityStarter.Request request, int phase,
+            LaunchParamsController.LaunchParams currentParams,
+            LaunchParamsController.LaunchParams outParams) {
+
+        initLogBuilder(task, activity);
+        int result = calculate(task, layout, activity, source, options, request, phase,
+                currentParams, outParams);
+        outputLog();
+        return result;
+    }
+
+    private int calculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
+            @Nullable ActivityRecord activity, @Nullable ActivityRecord source,
+            @Nullable ActivityOptions options, @Nullable ActivityStarter.Request request, int phase,
+            LaunchParamsController.LaunchParams currentParams,
+            LaunchParamsController.LaunchParams outParams) {
+
+        if (task == null) {
+            appendLog("task null, skipping");
+            return RESULT_SKIP;
+        }
+        if (phase != PHASE_BOUNDS) {
+            appendLog("not in bounds phase, skipping");
+            return RESULT_SKIP;
+        }
+        if (!task.inFreeformWindowingMode()) {
+            appendLog("not a freeform task, skipping");
+            return RESULT_SKIP;
+        }
+        if (!currentParams.mBounds.isEmpty()) {
+            appendLog("currentParams has bounds set, not overriding");
+            return RESULT_SKIP;
+        }
+
+        // Copy over any values
+        outParams.set(currentParams);
+
+        // Update width and height with default desktop mode values
+        float density = (float) task.getConfiguration().densityDpi / DENSITY_DEFAULT;
+        final int width = (int) (DESKTOP_MODE_DEFAULT_WIDTH_DP * density + 0.5f);
+        final int height = (int) (DESKTOP_MODE_DEFAULT_HEIGHT_DP * density + 0.5f);
+        outParams.mBounds.right = width;
+        outParams.mBounds.bottom = height;
+
+        // Center the task in window bounds
+        Rect windowBounds = task.getWindowConfiguration().getBounds();
+        outParams.mBounds.offset(windowBounds.centerX() - outParams.mBounds.centerX(),
+                windowBounds.centerY() - outParams.mBounds.centerY());
+
+        appendLog("setting desktop mode task bounds to %s", outParams.mBounds);
+
+        return RESULT_DONE;
+    }
+
+    private void initLogBuilder(Task task, ActivityRecord activity) {
+        if (DEBUG) {
+            mLogBuilder = new StringBuilder(
+                    "DesktopModeLaunchParamsModifier: task=" + task + " activity=" + activity);
+        }
+    }
+
+    private void appendLog(String format, Object... args) {
+        if (DEBUG) mLogBuilder.append(" ").append(String.format(format, args));
+    }
+
+    private void outputLog() {
+        if (DEBUG) Slog.d(TAG, mLogBuilder.toString());
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java
new file mode 100644
index 0000000..a6f8557
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DeviceStateController.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 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.wm;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.function.Consumer;
+
+/**
+ * Class that registers callbacks with the {@link DeviceStateManager} and
+ * responds to fold state changes by forwarding such events to a delegate.
+ */
+final class DeviceStateController {
+    private final DeviceStateManager mDeviceStateManager;
+    private final Context mContext;
+
+    private FoldStateListener mDeviceStateListener;
+
+    public enum FoldState {
+        UNKNOWN, OPEN, FOLDED, HALF_FOLDED
+    }
+
+    DeviceStateController(Context context, Handler handler, Consumer<FoldState> delegate) {
+        mContext = context;
+        mDeviceStateManager = mContext.getSystemService(DeviceStateManager.class);
+        if (mDeviceStateManager != null) {
+            mDeviceStateListener = new FoldStateListener(mContext, delegate);
+            mDeviceStateManager
+                    .registerCallback(new HandlerExecutor(handler),
+                            mDeviceStateListener);
+        }
+    }
+
+    void unregisterFromDeviceStateManager() {
+        if (mDeviceStateListener != null) {
+            mDeviceStateManager.unregisterCallback(mDeviceStateListener);
+        }
+    }
+
+    /**
+     * A listener for half-fold device state events that dispatches state changes to a delegate.
+     */
+    static final class FoldStateListener implements DeviceStateManager.DeviceStateCallback {
+
+        private final int[] mHalfFoldedDeviceStates;
+        private final int[] mFoldedDeviceStates;
+
+        @Nullable
+        private FoldState mLastResult;
+        private final Consumer<FoldState> mDelegate;
+
+        FoldStateListener(Context context, Consumer<FoldState> delegate) {
+            mFoldedDeviceStates = context.getResources().getIntArray(
+                    com.android.internal.R.array.config_foldedDeviceStates);
+            mHalfFoldedDeviceStates = context.getResources().getIntArray(
+                    com.android.internal.R.array.config_halfFoldedDeviceStates);
+            mDelegate = delegate;
+        }
+
+        @Override
+        public void onStateChanged(int state) {
+            final boolean halfFolded = ArrayUtils.contains(mHalfFoldedDeviceStates, state);
+            FoldState result;
+            if (halfFolded) {
+                result = FoldState.HALF_FOLDED;
+            } else {
+                final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
+                result = folded ? FoldState.FOLDED : FoldState.OPEN;
+            }
+            if (mLastResult == null || !mLastResult.equals(result)) {
+                mLastResult = result;
+                mDelegate.accept(result);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6c5222b..3c847ce 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -570,6 +570,7 @@
     final FixedRotationTransitionListener mFixedRotationTransitionListener =
             new FixedRotationTransitionListener();
 
+    private final DeviceStateController mDeviceStateController;
     private final PhysicalDisplaySwitchTransitionLauncher mDisplaySwitchTransitionLauncher;
     final RemoteDisplayChangeController mRemoteDisplayChangeController;
 
@@ -1125,6 +1126,13 @@
 
         mDisplayPolicy = new DisplayPolicy(mWmService, this);
         mDisplayRotation = new DisplayRotation(mWmService, this);
+
+        mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH,
+                newFoldState -> {
+                    mDisplaySwitchTransitionLauncher.foldStateChanged(newFoldState);
+                    mDisplayRotation.foldStateChanged(newFoldState);
+                });
+
         mCloseToSquareMaxAspectRatio = mWmService.mContext.getResources().getFloat(
                 R.dimen.config_closeToSquareDisplayMaxAspectRatio);
         if (isDefaultDisplay) {
@@ -3253,7 +3261,7 @@
             mTransitionController.unregisterLegacyListener(mFixedRotationTransitionListener);
             handleAnimatingStoppedAndTransition();
             mWmService.stopFreezingDisplayLocked();
-            mDisplaySwitchTransitionLauncher.destroy();
+            mDeviceStateController.unregisterFromDeviceStateManager();
             super.removeImmediately();
             if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
             mPointerEventDispatcher.dispose();
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 97609a7..a8d13c5 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -40,6 +40,7 @@
 
 import android.annotation.AnimRes;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -108,6 +109,8 @@
     private OrientationListener mOrientationListener;
     private StatusBarManagerInternal mStatusBarManagerInternal;
     private SettingsObserver mSettingsObserver;
+    @Nullable
+    private FoldController mFoldController;
 
     @ScreenOrientation
     private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
@@ -238,6 +241,10 @@
             mOrientationListener.setCurrentRotation(mRotation);
             mSettingsObserver = new SettingsObserver(uiHandler);
             mSettingsObserver.observe();
+            if (mSupportAutoRotation && mContext.getResources().getBoolean(
+                    R.bool.config_windowManagerHalfFoldAutoRotateOverride)) {
+                mFoldController = new FoldController();
+            }
         }
     }
 
@@ -436,7 +443,17 @@
 
         final int oldRotation = mRotation;
         final int lastOrientation = mLastOrientation;
-        final int rotation = rotationForOrientation(lastOrientation, oldRotation);
+        int rotation = rotationForOrientation(lastOrientation, oldRotation);
+        // Use the saved rotation for tabletop mode, if set.
+        if (mFoldController != null && mFoldController.shouldRevertOverriddenRotation()) {
+            int prevRotation = rotation;
+            rotation = mFoldController.revertOverriddenRotation();
+            ProtoLog.v(WM_DEBUG_ORIENTATION,
+                    "Reverting orientation. Rotating to %s from %s rather than %s.",
+                    Surface.rotationToString(rotation),
+                    Surface.rotationToString(oldRotation),
+                    Surface.rotationToString(prevRotation));
+        }
         ProtoLog.v(WM_DEBUG_ORIENTATION,
                 "Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and "
                         + "oldRotation=%s (%d)",
@@ -1138,7 +1155,8 @@
             // If we don't support auto-rotation then bail out here and ignore
             // the sensor and any rotation lock settings.
             preferredRotation = -1;
-        } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+        } else if (((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+                            || isTabletopAutoRotateOverrideEnabled())
                         && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
                                 || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
                                 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
@@ -1292,10 +1310,17 @@
         return false;
     }
 
+    private boolean isTabletopAutoRotateOverrideEnabled() {
+        return mFoldController != null && mFoldController.overrideFrozenRotation();
+    }
+
     private boolean isRotationChoicePossible(int orientation) {
         // Rotation choice is only shown when the user is in locked mode.
         if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
 
+        // Don't show rotation choice if we are in tabletop or book modes.
+        if (isTabletopAutoRotateOverrideEnabled()) return false;
+
         // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
         // demo, hdmi, vr, etc mode.
 
@@ -1496,6 +1521,74 @@
         proto.end(token);
     }
 
+    /**
+     * Called by the DeviceStateManager callback when the device state changes.
+     */
+    void foldStateChanged(DeviceStateController.FoldState foldState) {
+        if (mFoldController != null) {
+            synchronized (mLock) {
+                mFoldController.foldStateChanged(foldState);
+            }
+        }
+    }
+
+    private class FoldController {
+        @Surface.Rotation
+        private int mHalfFoldSavedRotation = -1; // No saved rotation
+        private DeviceStateController.FoldState mFoldState =
+                DeviceStateController.FoldState.UNKNOWN;
+
+        boolean overrideFrozenRotation() {
+            return mFoldState == DeviceStateController.FoldState.HALF_FOLDED;
+        }
+
+        boolean shouldRevertOverriddenRotation() {
+            return mFoldState == DeviceStateController.FoldState.OPEN // When transitioning to open.
+                    && mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
+                    && mUserRotationMode
+                    == WindowManagerPolicy.USER_ROTATION_LOCKED; // Ignore if we're unlocked.
+        }
+
+        int revertOverriddenRotation() {
+            int savedRotation = mHalfFoldSavedRotation;
+            mHalfFoldSavedRotation = -1;
+            return savedRotation;
+        }
+
+        void foldStateChanged(DeviceStateController.FoldState newState) {
+            ProtoLog.v(WM_DEBUG_ORIENTATION,
+                    "foldStateChanged: displayId %d, halfFoldStateChanged %s, "
+                    + "saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, "
+                    + "mLastOrientation: %d, mRotation: %d",
+                    mDisplayContent.getDisplayId(), newState.name(), mHalfFoldSavedRotation,
+                    mUserRotation, mLastSensorRotation, mLastOrientation, mRotation);
+            if (mFoldState == DeviceStateController.FoldState.UNKNOWN) {
+                mFoldState = newState;
+                return;
+            }
+            if (newState == DeviceStateController.FoldState.HALF_FOLDED
+                    && mFoldState != DeviceStateController.FoldState.HALF_FOLDED) {
+                // The device has transitioned to HALF_FOLDED state: save the current rotation and
+                // update the device rotation.
+                mHalfFoldSavedRotation = mRotation;
+                mFoldState = newState;
+                // Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
+                // return true, so rotation is unlocked.
+                mService.updateRotation(false /* alwaysSendConfiguration */,
+                        false /* forceRelayout */);
+            } else {
+                // Revert the rotation to our saved value if we transition from HALF_FOLDED.
+                mRotation = mHalfFoldSavedRotation;
+                // Tell the device to update its orientation (mFoldState is still HALF_FOLDED here
+                // so we will override USER_ROTATION_LOCKED and allow a rotation).
+                mService.updateRotation(false /* alwaysSendConfiguration */,
+                        false /* forceRelayout */);
+                // Once we are rotated, set mFoldstate, effectively removing the lock override.
+                mFoldState = newState;
+            }
+        }
+    }
+
     private class OrientationListener extends WindowOrientationListener implements Runnable {
         transient boolean mEnabled;
 
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index 7bd2a4a..e74e5787 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -64,6 +64,10 @@
     void registerDefaultModifiers(ActivityTaskSupervisor supervisor) {
         // {@link TaskLaunchParamsModifier} handles window layout preferences.
         registerModifier(new TaskLaunchParamsModifier(supervisor));
+        if (DesktopModeLaunchParamsModifier.DESKTOP_MODE_SUPPORTED) {
+            // {@link DesktopModeLaunchParamsModifier} handles default task size changes
+            registerModifier(new DesktopModeLaunchParamsModifier());
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
index a89894d..30bdc34 100644
--- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
+++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
@@ -24,10 +24,7 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
 import android.graphics.Rect;
-import android.hardware.devicestate.DeviceStateManager;
-import android.os.HandlerExecutor;
 import android.window.DisplayAreaInfo;
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
@@ -36,11 +33,8 @@
 
     private final DisplayContent mDisplayContent;
     private final WindowManagerService mService;
-    private final DeviceStateManager mDeviceStateManager;
     private final TransitionController mTransitionController;
 
-    private DeviceStateListener mDeviceStateListener;
-
     /**
      * If on a foldable device represents whether the device is folded or not
      */
@@ -52,21 +46,15 @@
         mDisplayContent = displayContent;
         mService = displayContent.mWmService;
         mTransitionController = transitionController;
-
-        mDeviceStateManager = mService.mContext.getSystemService(DeviceStateManager.class);
-
-        if (mDeviceStateManager != null) {
-            mDeviceStateListener = new DeviceStateListener(mService.mContext);
-            mDeviceStateManager
-                    .registerCallback(new HandlerExecutor(mDisplayContent.mWmService.mH),
-                            mDeviceStateListener);
-        }
     }
 
-    public void destroy() {
-        if (mDeviceStateManager != null) {
-            mDeviceStateManager.unregisterCallback(mDeviceStateListener);
-        }
+    /**
+     *   Called by the DeviceStateManager callback when the state changes.
+     */
+    void foldStateChanged(DeviceStateController.FoldState newFoldState) {
+        // Ignore transitions to/from half-folded.
+        if (newFoldState == DeviceStateController.FoldState.HALF_FOLDED) return;
+        mIsFolded = newFoldState == DeviceStateController.FoldState.FOLDED;
     }
 
     /**
@@ -143,10 +131,4 @@
         mTransition = null;
     }
 
-    class DeviceStateListener extends DeviceStateManager.FoldStateListener {
-
-        DeviceStateListener(Context context) {
-            super(context, newIsFolded -> mIsFolded = newIsFolded);
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 38c7595..3c55396 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2020,7 +2020,12 @@
                 // non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore
                 // to its previous freeform bounds.
                 rootTask.setLastNonFullscreenBounds(task.mLastNonFullscreenBounds);
-                rootTask.setBounds(task.getBounds());
+                // When creating a new Task for PiP, set its initial bounds as the TaskFragment in
+                // case the activity is embedded, so that it can be animated to PiP window from the
+                // current bounds.
+                // Use Task#setBoundsUnchecked to skip checking windowing mode as the windowing mode
+                // will be updated later after this is collected in transition.
+                rootTask.setBoundsUnchecked(r.getTaskFragment().getBounds());
 
                 // Move the last recents animation transaction from original task to the new one.
                 if (task.mLastRecentsAnimationTransaction != null) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ebc8ae0..885968f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2605,6 +2605,13 @@
         return boundsChange;
     }
 
+    /** Sets the requested bounds regardless of the windowing mode. */
+    int setBoundsUnchecked(@NonNull Rect bounds) {
+        final int boundsChange = super.setBounds(bounds);
+        updateSurfaceBounds();
+        return boundsChange;
+    }
+
     @Override
     public boolean isCompatible(int windowingMode, int activityType) {
         // TODO: Should we just move this to ConfigurationContainer?
@@ -5852,10 +5859,7 @@
             return BOUNDS_CHANGE_NONE;
         }
 
-        final int result = super.setBounds(!inMultiWindowMode() ? null : bounds);
-
-        updateSurfaceBounds();
-        return result;
+        return setBoundsUnchecked(!inMultiWindowMode() ? null : bounds);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index cb29e3f..ab38ed23 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1561,6 +1561,10 @@
             if (info.mEndParent != null) {
                 change.setParent(info.mEndParent.mRemoteToken.toWindowContainerToken());
             }
+            if (info.mStartParent != null && info.mStartParent.mRemoteToken != null
+                    && target.getParent() != info.mStartParent) {
+                change.setLastParent(info.mStartParent.mRemoteToken.toWindowContainerToken());
+            }
             change.setMode(info.getTransitMode(target));
             change.setStartAbsBounds(info.mAbsoluteBounds);
             change.setFlags(info.getChangeFlags(target));
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ad3f045..961c320 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -423,7 +423,7 @@
      * @see #ENABLE_SHELL_TRANSITIONS
      */
     public static final boolean sEnableShellTransitions =
-            SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, true);
+            SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, false);
 
     /**
      * Allows a fullscreen windowing mode activity to launch in its desired orientation directly
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ebdef8a..a09d994 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -58,6 +58,7 @@
         // TODO: remove once Android migrates to JUnit 4.12,
         // which provides assertThrows
         "testng",
+        "truth-prebuilt",
         "junit",
         "junit-params",
         "platform-compat-test-rules",
diff --git a/services/tests/servicestests/res/xml/irq_device_map_1.xml b/services/tests/servicestests/res/xml/irq_device_map_1.xml
new file mode 100644
index 0000000..1f1a77b
--- /dev/null
+++ b/services/tests/servicestests/res/xml/irq_device_map_1.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, 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.
+*/
+-->
+<irq-device-map>
+    <device name="test.device.1">
+        <subsystem>test.subsystem.1</subsystem>
+        <subsystem>test.subsystem.2</subsystem>
+    </device>
+    <device name="test.device.2">
+        <subsystem>test.subsystem.3</subsystem>
+        <subsystem>test.subsystem.2</subsystem>
+    </device>
+</irq-device-map>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/irq_device_map_2.xml b/services/tests/servicestests/res/xml/irq_device_map_2.xml
new file mode 100644
index 0000000..508c98d
--- /dev/null
+++ b/services/tests/servicestests/res/xml/irq_device_map_2.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, 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.
+*/
+-->
+
+<!-- Long comment describing anything that may be needed
+for this file and its maintenance -->
+<irq-device-map>
+    <!-- Small comment specific to this device -->
+    <invalid name="test.device.1">
+        <!-- These valid subsystem definitions should be ignored because of invalid parent tag -->
+        <subsystem>test.subsystem.1</subsystem>
+        <subsystem>test.subsystem.2</subsystem>
+    </invalid>
+    <device name="test.device.2">
+        <!-- Multiline comment to describe nuances
+         about why these subsystems
+         rely on this hardware device
+         to wake the CPU up from sleep
+        -->
+        <subsystem>test.subsystem.3</subsystem>
+        <!-- Small comment specific to test.subsystem.4 -->
+        <subsystem>test.subsystem.4</subsystem>
+        <subsystem>test.subsystem.5</subsystem>
+        <!-- Duplicates should be ignored -->
+        <subsystem>test.subsystem.4</subsystem>
+        <subsystem>test.subsystem.3</subsystem>
+        <subsystem>test.subsystem.5</subsystem>
+    </device>
+
+    <device name="test.device.3">
+        <!-- All child tags are invalid, mapping should be empty / non-existent for this device -->
+        <subsys>ignored</subsys>
+        <system>redundant</system>
+    </device>
+
+    <device name="test.device.4">
+        <!-- Invalid child tags should be skipped but others should be mapped -->
+        <invalid>unused</invalid>
+        <random>skipped</random>
+        <subsystem>test.subsystem.1</subsystem>
+        <subsystem>test.subsystem.4</subsystem>
+    </device>
+
+</irq-device-map>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/irq_device_map_3.xml b/services/tests/servicestests/res/xml/irq_device_map_3.xml
new file mode 100644
index 0000000..498b676
--- /dev/null
+++ b/services/tests/servicestests/res/xml/irq_device_map_3.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, 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.
+*/
+-->
+<irq-device-map>
+    <device name="test.alarm.device">
+        <subsystem>Alarm</subsystem>
+    </device>
+    <device name="test.wifi.device">
+        <subsystem>undefined</subsystem>
+    </device>
+</irq-device-map>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java b/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
index 1171518..581a2a7 100644
--- a/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
@@ -41,7 +41,6 @@
 import org.junit.runner.RunWith;
 
 import java.util.List;
-import java.util.concurrent.CancellationException;
 
 @Presubmit
 @RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index 27c3ca4..a545b1f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -325,6 +325,7 @@
                 actual.getSessionErrorMessage());
         assertEquals(expected.isPrepared(), actual.isPrepared());
         assertEquals(expected.isCommitted(), actual.isCommitted());
+        assertEquals(expected.isPreapprovalRequested(), actual.isPreapprovalRequested());
         assertEquals(expected.createdMillis, actual.createdMillis);
         assertEquals(expected.isSealed(), actual.isSealed());
         assertEquals(expected.isMultiPackage(), actual.isMultiPackage());
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
new file mode 100644
index 0000000..7731a32
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 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.power.stats;
+
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
+
+import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
+import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_RETENTION_MS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.frameworks.servicestests.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+
+@RunWith(AndroidJUnit4.class)
+public class CpuWakeupStatsTest {
+    private static final String KERNEL_REASON_ALARM_IRQ = "120 test.alarm.device";
+    private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
+    private static final String KERNEL_REASON_UNKNOWN = "unsupported-free-form-reason";
+
+    private static final int TEST_UID_1 = 13239823;
+    private static final int TEST_UID_2 = 25268423;
+    private static final int TEST_UID_3 = 92261423;
+    private static final int TEST_UID_4 = 56926423;
+    private static final int TEST_UID_5 = 76421423;
+
+    private static final Context sContext = InstrumentationRegistry.getTargetContext();
+    private final ThreadLocalRandom mRandom = ThreadLocalRandom.current();
+
+    @Test
+    public void removesOldWakeups() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_1);
+
+        final Set<Long> timestamps = new HashSet<>();
+        final long firstWakeup = 453192;
+
+        obj.noteWakeupTimeAndReason(firstWakeup, 32, "unused");
+        timestamps.add(firstWakeup);
+        for (int i = 1; i < 1000; i++) {
+            final long delta = mRandom.nextLong(WAKEUP_RETENTION_MS);
+            if (timestamps.add(firstWakeup + delta)) {
+                obj.noteWakeupTimeAndReason(firstWakeup + delta, i, "unused");
+            }
+        }
+        assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
+
+        obj.noteWakeupTimeAndReason(firstWakeup + WAKEUP_RETENTION_MS + 1242, 231, "unused");
+        assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
+
+        for (int i = 0; i < 100; i++) {
+            final long now = mRandom.nextLong(WAKEUP_RETENTION_MS + 1, 100 * WAKEUP_RETENTION_MS);
+            obj.noteWakeupTimeAndReason(now, i, "unused");
+            assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - WAKEUP_RETENTION_MS))
+                    .isLessThan(0);
+        }
+    }
+
+    @Test
+    public void alarmIrqAttributionSolo() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+        final long wakeupTime = 12423121;
+
+        obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_ALARM_IRQ);
+
+        // Outside the window, so should be ignored.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+                wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+                wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+        // Should be attributed
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3, TEST_UID_5);
+
+        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        assertThat(attribution).isNotNull();
+        assertThat(attribution.size()).isEqualTo(1);
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+    }
+
+    @Test
+    public void alarmIrqAttributionCombined() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+        final long wakeupTime = 92123210;
+
+        obj.noteWakeupTimeAndReason(wakeupTime, 4,
+                KERNEL_REASON_UNKNOWN_IRQ + ":" + KERNEL_REASON_ALARM_IRQ);
+
+        // Outside the window, so should be ignored.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+                wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+                wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+        // Should be attributed
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4,
+                TEST_UID_5);
+
+        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        assertThat(attribution).isNotNull();
+        assertThat(attribution.size()).isEqualTo(2);
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
+    }
+
+    @Test
+    public void unknownIrqAttribution() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+        final long wakeupTime = 92123410;
+
+        obj.noteWakeupTimeAndReason(wakeupTime, 24, KERNEL_REASON_UNKNOWN_IRQ);
+
+        // Unrelated subsystems, should not be attributed
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4,
+                TEST_UID_5);
+
+        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        assertThat(attribution).isNotNull();
+        assertThat(attribution.size()).isEqualTo(1);
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
+        final SparseBooleanArray uids = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
+        assertThat(uids == null || uids.size() == 0).isTrue();
+    }
+
+    @Test
+    public void unknownAttribution() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+        final long wakeupTime = 72123210;
+
+        obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN);
+
+        // Unrelated subsystems, should be ignored.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
+
+        // There should be nothing in the attribution map.
+        assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java b/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java
new file mode 100644
index 0000000..43d9e60
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.frameworks.servicestests.R;
+import com.android.internal.util.CollectionUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class IrqDeviceMapTest {
+    private static final String TEST_DEVICE_1 = "test.device.1";
+    private static final String TEST_DEVICE_2 = "test.device.2";
+    private static final String TEST_DEVICE_3 = "test.device.3";
+    private static final String TEST_DEVICE_4 = "test.device.4";
+
+    private static final String TEST_SUBSYSTEM_1 = "test.subsystem.1";
+    private static final String TEST_SUBSYSTEM_2 = "test.subsystem.2";
+    private static final String TEST_SUBSYSTEM_3 = "test.subsystem.3";
+    private static final String TEST_SUBSYSTEM_4 = "test.subsystem.4";
+    private static final String TEST_SUBSYSTEM_5 = "test.subsystem.5";
+
+    private static final Context sContext = InstrumentationRegistry.getTargetContext();
+
+    @Test
+    public void cachesInstancesPerXml() {
+        IrqDeviceMap irqDeviceMap1 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_1);
+        IrqDeviceMap irqDeviceMap2 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_1);
+        assertThat(irqDeviceMap1).isSameInstanceAs(irqDeviceMap2);
+
+        irqDeviceMap2 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_2);
+        assertThat(irqDeviceMap1).isNotSameInstanceAs(irqDeviceMap2);
+
+        irqDeviceMap1 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_2);
+        assertThat(irqDeviceMap1).isSameInstanceAs(irqDeviceMap2);
+    }
+
+    @Test
+    public void simpleXml() {
+        IrqDeviceMap deviceMap = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_1);
+
+        List<String> subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_1);
+        assertThat(subsystems).hasSize(2);
+        // No specific order is required.
+        assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_2, TEST_SUBSYSTEM_1);
+
+        subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_2);
+        assertThat(subsystems).hasSize(2);
+        // No specific order is required.
+        assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_2, TEST_SUBSYSTEM_3);
+    }
+
+    @Test
+    public void complexXml() {
+        IrqDeviceMap deviceMap = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_2);
+
+        List<String> subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_1);
+        assertThat(CollectionUtils.isEmpty(subsystems)).isTrue();
+
+        subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_2);
+        assertThat(subsystems).hasSize(3);
+        // No specific order is required.
+        assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_3, TEST_SUBSYSTEM_4,
+                TEST_SUBSYSTEM_5);
+
+        subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_3);
+        assertThat(CollectionUtils.isEmpty(subsystems)).isTrue();
+
+        subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_4);
+        assertThat(subsystems).hasSize(2);
+        // No specific order is required.
+        assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_4, TEST_SUBSYSTEM_1);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java b/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
new file mode 100644
index 0000000..0b27f87
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 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.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.junit.runners.Parameterized;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Collection;
+
+@SmallTest
+@RunWith(Enclosed.class)
+public class EventLoggerTest {
+
+    private static final int EVENTS_LOGGER_SIZE = 3;
+    private static final String EVENTS_LOGGER_TAG = "TestLogger";
+
+    private static final TestEvent TEST_EVENT_1 = new TestEvent();
+    private static final TestEvent TEST_EVENT_2 = new TestEvent();
+    private static final TestEvent TEST_EVENT_3 = new TestEvent();
+    private static final TestEvent TEST_EVENT_4 = new TestEvent();
+    private static final TestEvent TEST_EVENT_5 = new TestEvent();
+
+    @RunWith(JUnit4.class)
+    public static class BasicOperationsTest {
+
+        private StringWriter mTestStringWriter;
+        private PrintWriter mTestPrintWriter;
+
+        private EventLogger mEventLogger;
+
+        @Before
+        public void setUp() {
+            mTestStringWriter = new StringWriter();
+            mTestPrintWriter = new PrintWriter(mTestStringWriter);
+            mEventLogger = new EventLogger(EVENTS_LOGGER_SIZE, EVENTS_LOGGER_TAG);
+        }
+
+        @Test
+        public void testThatConsumeOfEmptyLoggerProducesEmptyList() {
+            mEventLogger.dump(mTestPrintWriter);
+            assertThat(mTestStringWriter.toString()).isEmpty();
+        }
+    }
+
+    @RunWith(Parameterized.class)
+    public static class LoggingOperationTest {
+
+        @Parameterized.Parameters
+        public static Collection<Object[]> data() {
+            return Arrays.asList(new Object[][] {
+                    {
+                        // insertion order, max size is 3
+                        new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2 },
+                        // expected events
+                        new EventLogger.Event[] { TEST_EVENT_2, TEST_EVENT_1 }
+                    },
+                    {
+                        // insertion order, max size is 3
+                        new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_3, TEST_EVENT_2 },
+                        // expected events
+                        new EventLogger.Event[] { TEST_EVENT_2, TEST_EVENT_3, TEST_EVENT_1 }
+                    },
+                    {
+                        // insertion order, max size is 3
+                        new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2, TEST_EVENT_3,
+                            TEST_EVENT_4 },
+                        // expected events
+                        new EventLogger.Event[] { TEST_EVENT_4, TEST_EVENT_3, TEST_EVENT_2 }
+                    },
+                    {
+                        // insertion order, max size is 3
+                        new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2, TEST_EVENT_3,
+                            TEST_EVENT_4, TEST_EVENT_5 },
+                        // expected events
+                        new EventLogger.Event[] { TEST_EVENT_5, TEST_EVENT_4, TEST_EVENT_3 }
+                    }
+            });
+        }
+
+        private EventLogger mEventLogger;
+
+        private final StringWriter mTestStringWriter;
+        private final PrintWriter mTestPrintWriter;
+        private final EventLogger.Event[] mEventsToInsert;
+        private final EventLogger.Event[] mExpectedEvents;
+
+        public LoggingOperationTest(EventLogger.Event[] eventsToInsert,
+                EventLogger.Event[] expectedEvents) {
+            mTestStringWriter = new StringWriter();
+            mTestPrintWriter = new PrintWriter(mTestStringWriter);
+            mEventsToInsert = eventsToInsert;
+            mExpectedEvents = expectedEvents;
+        }
+
+        @Before
+        public void setUp() {
+            mEventLogger = new EventLogger(EVENTS_LOGGER_SIZE, EVENTS_LOGGER_TAG);
+        }
+
+        @Test
+        public void testThatLoggingWorksAsExpected() {
+            for (EventLogger.Event event: mEventsToInsert) {
+                mEventLogger.log(event);
+            }
+
+            mEventLogger.dump(mTestPrintWriter);
+
+            assertThat(mTestStringWriter.toString())
+                    .isEqualTo(convertEventsToString(mExpectedEvents));
+        }
+
+    }
+
+    private static String convertEventsToString(EventLogger.Event[] events) {
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        printWriter.println("Events log: " + EVENTS_LOGGER_TAG);
+
+        for (EventLogger.Event event: events) {
+            printWriter.println(event.toString());
+        }
+
+        return stringWriter.toString();
+    }
+
+    private static class TestEvent extends EventLogger.Event {
+
+        @Override
+        public String eventToString() {
+            return getClass().getName() + "@" + Integer.toHexString(hashCode());
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/utils/OWNERS b/services/tests/servicestests/src/com/android/server/utils/OWNERS
index 1853220..d1a36bb 100644
--- a/services/tests/servicestests/src/com/android/server/utils/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/utils/OWNERS
@@ -2,3 +2,5 @@
 per-file WatchableTester.java = shombert@google.com
 per-file WatcherTest.java = file:/services/core/java/com/android/server/pm/OWNERS
 per-file WatcherTest.java = shombert@google.com
+per-file EventLoggerTest.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS
+per-file EventLoggerTest.java = jmtrivi@google.com
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
new file mode 100644
index 0000000..7830e90
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 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.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.LaunchParamsController.LaunchParamsModifier.Result;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for desktop mode task bounds.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DesktopModeLaunchParamsModifierTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase {
+
+    private ActivityRecord mActivity;
+
+    private DesktopModeLaunchParamsModifier mTarget;
+
+    private LaunchParamsController.LaunchParams mCurrent;
+    private LaunchParamsController.LaunchParams mResult;
+
+    @Before
+    public void setUp() throws Exception {
+        mActivity = new ActivityBuilder(mAtm).build();
+        mTarget = new DesktopModeLaunchParamsModifier();
+        mCurrent = new LaunchParamsController.LaunchParams();
+        mCurrent.reset();
+        mResult = new LaunchParamsController.LaunchParams();
+        mResult.reset();
+    }
+
+    @Test
+    public void testReturnsSkipIfTaskIsNull() {
+        assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(null).calculate());
+    }
+
+    @Test
+    public void testReturnsSkipIfNotBoundsPhase() {
+        final Task task = new TaskBuilder(mSupervisor).build();
+        assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).setPhase(
+                PHASE_DISPLAY).calculate());
+    }
+
+    @Test
+    public void testReturnsSkipIfTaskNotInFreeform() {
+        final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
+                WINDOWING_MODE_FULLSCREEN).build();
+        assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
+    }
+
+    @Test
+    public void testReturnsSkipIfCurrentParamsHasBounds() {
+        final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
+                WINDOWING_MODE_FREEFORM).build();
+        mCurrent.mBounds.set(/* left */ 0, /* top */ 0, /* right */ 100, /* bottom */ 100);
+        assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
+    }
+
+    @Test
+    public void testUsesDefaultBounds() {
+        final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
+                WINDOWING_MODE_FREEFORM).build();
+        assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(dpiToPx(task, 840), mResult.mBounds.width());
+        assertEquals(dpiToPx(task, 630), mResult.mBounds.height());
+    }
+
+    @Test
+    public void testUsesDisplayAreaAndWindowingModeFromSource() {
+        final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
+                WINDOWING_MODE_FREEFORM).build();
+        TaskDisplayArea mockTaskDisplayArea = mock(TaskDisplayArea.class);
+        mCurrent.mPreferredTaskDisplayArea = mockTaskDisplayArea;
+        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
+
+        assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(mockTaskDisplayArea, mResult.mPreferredTaskDisplayArea);
+        assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
+    }
+
+    private int dpiToPx(Task task, int dpi) {
+        float density = (float) task.getConfiguration().densityDpi / DENSITY_DEFAULT;
+        return (int) (dpi * density + 0.5f);
+    }
+
+    private class CalculateRequestBuilder {
+        private Task mTask;
+        private int mPhase = PHASE_BOUNDS;
+        private final ActivityRecord mActivity =
+                DesktopModeLaunchParamsModifierTests.this.mActivity;
+        private final LaunchParamsController.LaunchParams mCurrentParams = mCurrent;
+        private final LaunchParamsController.LaunchParams mOutParams = mResult;
+
+        private CalculateRequestBuilder setTask(Task task) {
+            mTask = task;
+            return this;
+        }
+
+        private CalculateRequestBuilder setPhase(int phase) {
+            mPhase = phase;
+            return this;
+        }
+
+        @Result
+        private int calculate() {
+            return mTarget.onCalculate(mTask, /* layout*/ null, mActivity, /* source */
+                    null, /* options */ null, /* request */ null, mPhase, mCurrentParams,
+                    mOutParams);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
new file mode 100644
index 0000000..86732c9
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 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.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link DeviceStateController}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:DeviceStateControllerTests
+ */
+@SmallTest
+@Presubmit
+public class DeviceStateControllerTests {
+
+    private DeviceStateController.FoldStateListener mFoldStateListener;
+    private DeviceStateController mTarget;
+    private DeviceStateControllerBuilder mBuilder;
+
+    private Context mMockContext;
+    private Handler mMockHandler;
+    private Resources mMockRes;
+    private DeviceStateManager mMockDeviceStateManager;
+
+    private Consumer<DeviceStateController.FoldState> mDelegate;
+    private DeviceStateController.FoldState mCurrentState = DeviceStateController.FoldState.UNKNOWN;
+
+    @Before
+    public void setUp() {
+        mBuilder = new DeviceStateControllerBuilder();
+        mCurrentState = DeviceStateController.FoldState.UNKNOWN;
+    }
+
+    private void initialize(boolean supportFold, boolean supportHalfFold) throws Exception {
+        mBuilder.setSupportFold(supportFold, supportHalfFold);
+        mDelegate = (newFoldState) -> {
+            mCurrentState = newFoldState;
+        };
+        mBuilder.setDelegate(mDelegate);
+        mBuilder.build();
+        verifyFoldStateListenerRegistration(1);
+    }
+
+    @Test
+    public void testInitialization() throws Exception {
+        initialize(true /* supportFold */, true /* supportHalfFolded */);
+        mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
+        assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
+    }
+
+    @Test
+    public void testInitializationWithNoFoldSupport() throws Exception {
+        initialize(false /* supportFold */, false /* supportHalfFolded */);
+        mFoldStateListener.onStateChanged(mFoldedStates[0]);
+        // Note that the folded state is ignored.
+        assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
+    }
+
+    @Test
+    public void testWithFoldSupported() throws Exception {
+        initialize(true /* supportFold */, false /* supportHalfFolded */);
+        mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
+        assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
+        mFoldStateListener.onStateChanged(mFoldedStates[0]);
+        assertEquals(mCurrentState, DeviceStateController.FoldState.FOLDED);
+        mFoldStateListener.onStateChanged(mHalfFoldedStates[0]);
+        assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN); // Ignored
+    }
+
+    @Test
+    public void testWithHalfFoldSupported() throws Exception {
+        initialize(true /* supportFold */, true /* supportHalfFolded */);
+        mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
+        assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
+        mFoldStateListener.onStateChanged(mFoldedStates[0]);
+        assertEquals(mCurrentState, DeviceStateController.FoldState.FOLDED);
+        mFoldStateListener.onStateChanged(mHalfFoldedStates[0]);
+        assertEquals(mCurrentState, DeviceStateController.FoldState.HALF_FOLDED);
+    }
+
+
+    private final int[] mFoldedStates = {0};
+    private final int[] mUnfoldedStates = {1};
+    private final int[] mHalfFoldedStates = {2};
+
+
+    private void verifyFoldStateListenerRegistration(int numOfInvocation) {
+        final ArgumentCaptor<DeviceStateController.FoldStateListener> listenerCaptor =
+                ArgumentCaptor.forClass(DeviceStateController.FoldStateListener.class);
+        verify(mMockDeviceStateManager, times(numOfInvocation)).registerCallback(
+                any(),
+                listenerCaptor.capture());
+        if (numOfInvocation > 0) {
+            mFoldStateListener = listenerCaptor.getValue();
+        }
+    }
+
+    private class DeviceStateControllerBuilder {
+        private boolean mSupportFold = false;
+        private boolean mSupportHalfFold = false;
+        private Consumer<DeviceStateController.FoldState> mDelegate;
+
+        DeviceStateControllerBuilder setSupportFold(
+                boolean supportFold, boolean supportHalfFold) {
+            mSupportFold = supportFold;
+            mSupportHalfFold = supportHalfFold;
+            return this;
+        }
+
+        DeviceStateControllerBuilder setDelegate(
+                Consumer<DeviceStateController.FoldState> delegate) {
+            mDelegate = delegate;
+            return this;
+        }
+
+        private void mockFold(boolean enableFold, boolean enableHalfFold) {
+            if (enableFold) {
+                when(mMockContext.getResources().getIntArray(
+                        com.android.internal.R.array.config_foldedDeviceStates))
+                        .thenReturn(mFoldedStates);
+            }
+            if (enableHalfFold) {
+                when(mMockContext.getResources().getIntArray(
+                        com.android.internal.R.array.config_halfFoldedDeviceStates))
+                        .thenReturn(mHalfFoldedStates);
+            }
+        }
+
+        private void build() throws Exception {
+            mMockContext = mock(Context.class);
+            mMockRes = mock(Resources.class);
+            when(mMockContext.getResources()).thenReturn((mMockRes));
+            mMockDeviceStateManager = mock(DeviceStateManager.class);
+            when(mMockContext.getSystemService(DeviceStateManager.class))
+                    .thenReturn(mMockDeviceStateManager);
+            mockFold(mSupportFold, mSupportHalfFold);
+            mMockHandler = mock(Handler.class);
+            mTarget = new DeviceStateController(mMockContext, mMockHandler, mDelegate);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 89f7111..b45c37f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -28,6 +28,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atMost;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
@@ -103,7 +104,7 @@
     private Context mMockContext;
     private Resources mMockRes;
     private SensorManager mMockSensorManager;
-    private Sensor mFakeSensor;
+    private Sensor mFakeOrientationSensor;
     private DisplayWindowSettings mMockDisplayWindowSettings;
     private ContentResolver mMockResolver;
     private FakeSettingsProvider mFakeSettingsProvider;
@@ -323,7 +324,7 @@
         waitForUiHandler();
         verify(mMockSensorManager, times(numOfInvocation)).registerListener(
                 listenerCaptor.capture(),
-                same(mFakeSensor),
+                same(mFakeOrientationSensor),
                 anyInt(),
                 any());
         if (numOfInvocation > 0) {
@@ -460,7 +461,7 @@
                 SensorEvent.class.getDeclaredConstructor(int.class);
         constructor.setAccessible(true);
         final SensorEvent event = constructor.newInstance(1);
-        event.sensor = mFakeSensor;
+        event.sensor = mFakeOrientationSensor;
         event.values[0] = rotation;
         event.timestamp = SystemClock.elapsedRealtimeNanos();
         return event;
@@ -691,6 +692,43 @@
                 SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
     }
 
+    // ====================================================
+    // Tests for half-fold auto-rotate override of rotation
+    // ====================================================
+    @Test
+    public void testUpdatesRotationWhenSensorUpdates_RotationLocked_HalfFolded() throws Exception {
+        mBuilder.setSupportHalfFoldAutoRotateOverride(true);
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+        enableOrientationSensor();
+
+        mTarget.foldStateChanged(DeviceStateController.FoldState.OPEN);
+        freezeRotation(Surface.ROTATION_270);
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
+        assertTrue(waitForUiHandler());
+        // No rotation...
+        assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+        // ... until half-fold
+        mTarget.foldStateChanged(DeviceStateController.FoldState.HALF_FOLDED);
+        assertTrue(waitForUiHandler());
+        verify(sMockWm).updateRotation(false, false);
+        assertTrue(waitForUiHandler());
+        assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+        // ... then transition back to flat
+        mTarget.foldStateChanged(DeviceStateController.FoldState.OPEN);
+        assertTrue(waitForUiHandler());
+        verify(sMockWm, atLeast(1)).updateRotation(false, false);
+        assertTrue(waitForUiHandler());
+        assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+    }
+
     // =================================
     // Tests for Policy based Rotation
     // =================================
@@ -884,6 +922,7 @@
     private class DisplayRotationBuilder {
         private boolean mIsDefaultDisplay = true;
         private boolean mSupportAutoRotation = true;
+        private boolean mSupportHalfFoldAutoRotateOverride = false;
 
         private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
         private int mCarDockRotation;
@@ -920,6 +959,12 @@
             return this;
         }
 
+        private DisplayRotationBuilder setSupportHalfFoldAutoRotateOverride(
+                boolean supportHalfFoldAutoRotateOverride) {
+            mSupportHalfFoldAutoRotateOverride = supportHalfFoldAutoRotateOverride;
+            return this;
+        }
+
         private void captureObservers() {
             ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass(
                     ContentObserver.class);
@@ -1032,9 +1077,13 @@
             mMockSensorManager = mock(SensorManager.class);
             when(mMockContext.getSystemService(Context.SENSOR_SERVICE))
                     .thenReturn(mMockSensorManager);
-            mFakeSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION);
+            mFakeOrientationSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION);
             when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn(
-                    Collections.singletonList(mFakeSensor));
+                    Collections.singletonList(mFakeOrientationSensor));
+
+            when(mMockContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_windowManagerHalfFoldAutoRotateOverride))
+                    .thenReturn(mSupportHalfFoldAutoRotateOverride);
 
             mMockResolver = mock(ContentResolver.class);
             when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 4f03f54..e0e1d73 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -72,6 +72,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Rect;
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
@@ -391,6 +392,33 @@
         assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode());
     }
 
+    @Test
+    public void testMovingEmbeddedActivityToPip() {
+        final Rect taskBounds = new Rect(0, 0, 800, 1000);
+        final Rect taskFragmentBounds = new Rect(0, 0, 400, 1000);
+        final Task task = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        task.setBounds(taskBounds);
+        assertEquals(taskBounds, task.getBounds());
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .createActivityCount(2)
+                .setBounds(taskFragmentBounds)
+                .build();
+        assertEquals(taskFragmentBounds, taskFragment.getBounds());
+        final ActivityRecord topActivity = taskFragment.getTopMostActivity();
+
+        // Move the top activity to pinned root task.
+        mRootWindowContainer.moveActivityToPinnedRootTask(topActivity,
+                null /* launchIntoPipHostActivity */, "test");
+
+        final Task pinnedRootTask = task.getDisplayArea().getRootPinnedTask();
+
+        // Ensure the initial bounds of the PiP Task is the same as the TaskFragment.
+        ensureTaskPlacement(pinnedRootTask, topActivity);
+        assertEquals(taskFragmentBounds, pinnedRootTask.getBounds());
+    }
+
     private static void ensureTaskPlacement(Task task, ActivityRecord... activities) {
         final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ec6a74c..29a514c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1325,6 +1325,35 @@
     }
 
     @Test
+    public void testReparentChangeLastParent() {
+        final Transition transition = createTestTransition(TRANSIT_CHANGE);
+        final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+        final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+        // Reparent activity in transition.
+        final Task lastParent = createTask(mDisplayContent);
+        final Task newParent = createTask(mDisplayContent);
+        final ActivityRecord activity = createActivityRecord(lastParent);
+        activity.mVisibleRequested = true;
+        // Skip manipulate the SurfaceControl.
+        doNothing().when(activity).setDropInputMode(anyInt());
+        changes.put(activity, new Transition.ChangeInfo(activity));
+        activity.reparent(newParent, POSITION_TOP);
+        activity.mVisibleRequested = false;
+
+        participants.add(activity);
+        final ArrayList<WindowContainer> targets = Transition.calculateTargets(
+                participants, changes);
+        final TransitionInfo info = Transition.calculateTransitionInfo(
+                transition.mType, 0 /* flags */, targets, changes, mMockT);
+
+        // Change contains last parent info.
+        assertEquals(1, info.getChanges().size());
+        assertEquals(lastParent.mRemoteToken.toWindowContainerToken(),
+                info.getChanges().get(0).getLastParent());
+    }
+
+    @Test
     public void testIncludeEmbeddedActivityReparent() {
         final Transition transition = createTestTransition(TRANSIT_OPEN);
         final Task task = createTask(mDisplayContent);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 79546b8..151ff80 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1242,6 +1242,19 @@
         @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION)
         @Override
         public void updateState(
+                @Nullable PersistableBundle options,
+                @Nullable SharedMemory sharedMemory) {
+            synchronized (this) {
+                enforceIsCurrentVoiceInteractionService();
+
+                Binder.withCleanCallingIdentity(
+                        () -> mImpl.updateStateLocked(options, sharedMemory));
+            }
+        }
+
+        @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION)
+        @Override
+        public void initAndVerifyDetector(
                 @NonNull Identity voiceInteractorIdentity,
                 @Nullable PersistableBundle options,
                 @Nullable SharedMemory sharedMemory,
@@ -1250,21 +1263,12 @@
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService();
 
-                if (mImpl == null) {
-                    Slog.w(TAG, "updateState without running voice interaction service");
-                    return;
-                }
-
                 voiceInteractorIdentity.uid = Binder.getCallingUid();
                 voiceInteractorIdentity.pid = Binder.getCallingPid();
 
-                final long caller = Binder.clearCallingIdentity();
-                try {
-                    mImpl.updateStateLocked(
-                            voiceInteractorIdentity, options, sharedMemory, callback, detectorType);
-                } finally {
-                    Binder.restoreCallingIdentity(caller);
-                }
+                Binder.withCleanCallingIdentity(
+                        () -> mImpl.initAndVerifyDetectorLocked(voiceInteractorIdentity, options,
+                                sharedMemory, callback, detectorType));
             }
         }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index dcf7b78..5d1901d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -551,12 +551,31 @@
     }
 
     public void updateStateLocked(
+            @Nullable PersistableBundle options,
+            @Nullable SharedMemory sharedMemory) {
+        Slog.v(TAG, "updateStateLocked");
+
+        if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) {
+            Slog.w(TAG, "Can't set sharedMemory to be read-only");
+            throw new IllegalStateException("Can't set sharedMemory to be read-only");
+        }
+
+        if (mHotwordDetectionConnection == null) {
+            Slog.w(TAG, "update State, but no hotword detection connection");
+            throw new IllegalStateException("Hotword detection connection not found");
+        }
+        synchronized (mHotwordDetectionConnection.mLock) {
+            mHotwordDetectionConnection.updateStateLocked(options, sharedMemory);
+        }
+    }
+
+    public void initAndVerifyDetectorLocked(
             @NonNull Identity voiceInteractorIdentity,
             @Nullable PersistableBundle options,
             @Nullable SharedMemory sharedMemory,
             IHotwordRecognitionStatusCallback callback,
             int detectorType) {
-        Slog.v(TAG, "updateStateLocked");
+        Slog.v(TAG, "initAndVerifyDetectorLocked");
         int voiceInteractionServiceUid = mInfo.getServiceInfo().applicationInfo.uid;
         if (mHotwordDetectionComponentName == null) {
             Slog.w(TAG, "Hotword detection service name not found");
@@ -614,8 +633,6 @@
                     mInfo.getServiceInfo().applicationInfo.uid, voiceInteractorIdentity,
                     mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false,
                     options, sharedMemory, callback, detectorType);
-        } else {
-            mHotwordDetectionConnection.updateStateLocked(options, sharedMemory);
         }
     }