Add RollbackManagerInternal (4/n)

This is an effort to modularize RollbackManager.

AIDL interfaces (IRollbackManager in this case) can’t be used across
module boundaries. We need to add a wrapper so system_server classes
can depend on this wrapper instead of IRollbackManager.

See https://docs.google.com/document/d/1d60nWwTcwrLps8dWDkxtkcUxSNt6q1x9rgAKwG-xO9E/view#heading=h.vqwoptkbpscx
for more details.

Bug: 150347230
Test: atest StagedRollbackTest

Change-Id: Ie34489aa3276f75d192ac4a08112012ef7f94509
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index b92fb47..38ba47a 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -24,6 +24,8 @@
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Representation of a user on the device.
@@ -257,6 +259,26 @@
     }
 
     /** @hide */
+    @NonNull
+    public static int[] fromUserHandles(@NonNull List<UserHandle> users) {
+        int[] userIds = new int[users.size()];
+        for (int i = 0; i < userIds.length; ++i) {
+            userIds[i] = users.get(i).getIdentifier();
+        }
+        return userIds;
+    }
+
+    /** @hide */
+    @NonNull
+    public static List<UserHandle> toUserHandles(@NonNull int[] userIds) {
+        List<UserHandle> users = new ArrayList<>(userIds.length);
+        for (int i = 0; i < userIds.length; ++i) {
+            users.add(UserHandle.of(userIds[i]));
+        }
+        return users;
+    }
+
+    /** @hide */
     @TestApi
     @SystemApi
     public static UserHandle of(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7459042..69318d2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -236,7 +236,6 @@
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.res.Resources;
-import android.content.rollback.IRollbackManager;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.hardware.display.DisplayManager;
@@ -367,6 +366,7 @@
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.permission.PermissionsState;
 import com.android.server.policy.PermissionPolicyInternal;
+import com.android.server.rollback.RollbackManagerInternal;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
@@ -14314,8 +14314,7 @@
      */
     private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
             PostInstallData data) {
-        IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                ServiceManager.getService(Context.ROLLBACK_SERVICE));
+        RollbackManagerInternal rm = LocalServices.getService(RollbackManagerInternal.class);
 
         final String packageName = res.pkg.getPackageName();
         final int[] allUsers = mUserManager.getUserIds();
@@ -14343,9 +14342,9 @@
         if (ps != null && doSnapshotOrRestore) {
             final String seInfo = AndroidPackageUtils.getSeInfo(res.pkg, ps);
             try {
-                rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
-                        seInfo, token);
-            } catch (RemoteException re) {
+                rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
+                        appId, ceDataInode, seInfo, token);
+            } catch (RuntimeException re) {
                 Log.e(TAG, "Error snapshotting/restoring user data: " + re);
                 return false;
             }
@@ -21390,7 +21389,7 @@
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ShellCallback callback,
             ResultReceiver resultReceiver) {
-        (new PackageManagerShellCommand(this, mPermissionManagerService)).exec(
+        (new PackageManagerShellCommand(this, mPermissionManagerService, mContext)).exec(
                 this, in, out, err, args, callback, resultReceiver);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index ca68e31..d06a231 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -66,7 +66,6 @@
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
-import android.content.rollback.IRollbackManager;
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
@@ -145,6 +144,7 @@
 
     final IPackageManager mInterface;
     final IPermissionManager mPermissionManager;
+    final Context mShellPackageContext;
     final private WeakHashMap<String, Resources> mResourceCache =
             new WeakHashMap<String, Resources>();
     int mTargetUser;
@@ -153,9 +153,15 @@
     int mQueryFlags;
 
     PackageManagerShellCommand(
-            PackageManagerService service, IPermissionManager permissionManager) {
+            PackageManagerService service, IPermissionManager permissionManager, Context context) {
         mInterface = service;
         mPermissionManager = permissionManager;
+        try {
+            mShellPackageContext = context.createPackageContext("com.android.shell", 0);
+        } catch (NameNotFoundException e) {
+            // should not happen
+            throw new RuntimeException(e);
+        }
     }
 
     @Override
@@ -460,32 +466,25 @@
         }
 
         final LocalIntentReceiver receiver = new LocalIntentReceiver();
-        try {
-            IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
-
-            RollbackInfo rollback = null;
-            for (RollbackInfo r : (List<RollbackInfo>) rm.getAvailableRollbacks().getList()) {
-                for (PackageRollbackInfo info : r.getPackages()) {
-                    if (packageName.equals(info.getPackageName())) {
-                        rollback = r;
-                        break;
-                    }
+        RollbackManager rm = mShellPackageContext.getSystemService(RollbackManager.class);
+        RollbackInfo rollback = null;
+        for (RollbackInfo r : rm.getAvailableRollbacks()) {
+            for (PackageRollbackInfo info : r.getPackages()) {
+                if (packageName.equals(info.getPackageName())) {
+                    rollback = r;
+                    break;
                 }
             }
-
-            if (rollback == null) {
-                pw.println("No available rollbacks for: " + packageName);
-                return 1;
-            }
-
-            rm.commitRollback(rollback.getRollbackId(),
-                    ParceledListSlice.<VersionedPackage>emptyList(),
-                    "com.android.shell", receiver.getIntentSender());
-        } catch (RemoteException re) {
-            // Cannot happen.
         }
 
+        if (rollback == null) {
+            pw.println("No available rollbacks for: " + packageName);
+            return 1;
+        }
+
+        rm.commitRollback(rollback.getRollbackId(),
+                Collections.emptyList(), receiver.getIntentSender());
+
         final Intent result = receiver.getResult();
         final int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
                 RollbackManager.STATUS_FAILURE);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 9d81928..0ead1327 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -40,7 +40,6 @@
 import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.rollback.IRollbackManager;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 import android.os.Bundle;
@@ -52,7 +51,6 @@
 import android.os.ParcelableException;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
@@ -75,6 +73,7 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.rollback.RollbackManagerInternal;
 import com.android.server.rollback.WatchdogRollbackLogger;
 
 import java.io.File;
@@ -483,8 +482,7 @@
 
         final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
         final int[] allUsers = um.getUserIds();
-        IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                ServiceManager.getService(Context.ROLLBACK_SERVICE));
+        RollbackManagerInternal rm = LocalServices.getService(RollbackManagerInternal.class);
 
         for (int i = 0, sessionsSize = apexSessions.size(); i < sessionsSize; i++) {
             final String packageName = apexSessions.get(i).getPackageName();
@@ -500,18 +498,18 @@
     }
 
     private void snapshotAndRestoreApexUserData(
-            String packageName, int[] allUsers, IRollbackManager rm) {
+            String packageName, int[] allUsers, RollbackManagerInternal rm) {
         try {
             // appId, ceDataInode, and seInfo are not needed for APEXes
-            rm.snapshotAndRestoreUserData(packageName, allUsers, 0, 0,
+            rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(allUsers), 0, 0,
                     null, 0 /*token*/);
-        } catch (RemoteException re) {
+        } catch (RuntimeException re) {
             Slog.e(TAG, "Error snapshotting/restoring user data: " + re);
         }
     }
 
     private void snapshotAndRestoreApkInApexUserData(
-            String packageName, int[] allUsers, IRollbackManager rm) {
+            String packageName, int[] allUsers, RollbackManagerInternal rm) {
         PackageManagerInternal mPmi = LocalServices.getService(PackageManagerInternal.class);
         AndroidPackage pkg = mPmi.getPackage(packageName);
         if (pkg == null) {
@@ -532,9 +530,9 @@
 
             final String seInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
             try {
-                rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
-                        seInfo, 0 /*token*/);
-            } catch (RemoteException re) {
+                rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
+                        appId, ceDataInode, seInfo, 0 /*token*/);
+            } catch (RuntimeException re) {
                 Slog.e(TAG, "Error snapshotting/restoring user data: " + re);
             }
         }
@@ -878,11 +876,11 @@
         if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
             // If rollback is available for this session, notify the rollback
             // manager of the apk session so it can properly enable rollback.
-            final IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
+            final RollbackManagerInternal rm =
+                    LocalServices.getService(RollbackManagerInternal.class);
             try {
                 rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId);
-            } catch (RemoteException re) {
+            } catch (RuntimeException re) {
                 Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
                         + session.sessionId, re);
             }
@@ -1384,8 +1382,8 @@
                 // If rollback is enabled for this session, we call through to the RollbackManager
                 // with the list of sessions it must enable rollback for. Note that
                 // notifyStagedSession is a synchronous operation.
-                final IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                        ServiceManager.getService(Context.ROLLBACK_SERVICE));
+                final RollbackManagerInternal rm =
+                        LocalServices.getService(RollbackManagerInternal.class);
                 try {
                     // NOTE: To stay consistent with the non-staged install flow, we don't fail the
                     // entire install if rollbacks can't be enabled.
@@ -1395,7 +1393,7 @@
                             mSessionRollbackIds.put(session.sessionId, rollbackId);
                         }
                     }
-                } catch (RemoteException re) {
+                } catch (RuntimeException re) {
                     Slog.e(TAG, "Failed to notifyStagedSession for session: "
                             + session.sessionId, re);
                 }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerInternal.java b/services/core/java/com/android/server/rollback/RollbackManagerInternal.java
new file mode 100644
index 0000000..b473b8c
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/RollbackManagerInternal.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import android.annotation.NonNull;
+import android.os.UserHandle;
+
+import java.util.List;
+
+/**
+ * A partial interface of IRollbackManager used by the system server only.
+ *
+ * @hide
+ */
+public interface RollbackManagerInternal {
+    /**
+     * Exposed for use from the system server only. Callback from the package
+     * manager during the install flow when user data can be backed up and restored for a given
+     * package.
+     *
+     * @param packageName Name of the package to restore/backup user data for
+     * @param users Users whose data to be restored/backed up
+     * @param appId ID of the package to restore/backup user data for
+     * @param ceDataInode The index node of CE data to restore/backup
+     * @param seInfo The seinfo tag used by SELinux policy
+     * @param token Used to inform the package manager that the pending package install is finished
+     */
+    void snapshotAndRestoreUserData(@NonNull String packageName, @NonNull List<UserHandle> users,
+            int appId, long ceDataInode, @NonNull String seInfo, int token);
+
+    /**
+     * Used by the staging manager to notify the RollbackManager that a session is
+     * being staged. In the case of multi-package sessions, the specified sessionId
+     * is that of the parent session.
+     *
+     * NOTE: This call is synchronous.
+     *
+     * @param sessionId The session ID that is being staged
+     * @return The rollback id if rollback was enabled successfully, or -1 if not.
+     */
+    int notifyStagedSession(int sessionId);
+
+    /**
+     * Used by the staging manager to notify the RollbackManager of the apk
+     * session for a staged session.
+     *
+     * @param originalSessionId The original session ID where this apk session belongs
+     * @param apkSessionId The ID of this apk session
+     */
+    void notifyStagedApkSession(int originalSessionId, int apkSessionId);
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerService.java b/services/core/java/com/android/server/rollback/RollbackManagerService.java
index f7ba9bb..a2c66a4 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerService.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerService.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 /**
@@ -38,6 +39,7 @@
     public void onStart() {
         mService = new RollbackManagerServiceImpl(getContext());
         publishBinderService(Context.ROLLBACK_SERVICE, mService);
+        LocalServices.addService(RollbackManagerInternal.class, mService);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index e592053..b33e04c3 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -118,7 +118,7 @@
  *   mutually-exclusive to ensure @WorkerThread methods and @ExtThread ones never call into each
  *   other.
  */
-class RollbackManagerServiceImpl extends IRollbackManager.Stub {
+class RollbackManagerServiceImpl extends IRollbackManager.Stub implements RollbackManagerInternal {
     /**
      * Denotes that the annotated methods is intended for external entities and should be called on
      * an external thread. By 'external' we mean any thread that is not the handler thread.
@@ -927,6 +927,15 @@
 
     @ExtThread
     @Override
+    public void snapshotAndRestoreUserData(String packageName, List<UserHandle> users, int appId,
+            long ceDataInode, String seInfo, int token) {
+        assertNotInWorkerThread();
+        snapshotAndRestoreUserData(packageName, UserHandle.fromUserHandles(users), appId,
+                ceDataInode, seInfo, token);
+    }
+
+    @ExtThread
+    @Override
     public void snapshotAndRestoreUserData(String packageName, int[] userIds, int appId,
             long ceDataInode, String seInfo, int token) {
         assertNotInWorkerThread();