Prepare for switch to idmap2

Prepare the idmap and asset managers for interfacing with idmap2 instead
of today's installd + idmap pipeline, but don't make the switch just
yet.

Instead, idmap2 runs as its own native daemon with an AIDL interface.
This removes the need for installd to fork and exec on each idmap call,
saving about 50 ms per call.

Bug: 78815803
Test: atest OverlayDeviceTests OverlayHostTests
Change-Id: I60852e15d99329896ff9de6559d1e7cd1c67e33d
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 888ad1d..6174300 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -3,6 +3,7 @@
 
     aidl: {
         include_dirs: [
+            "frameworks/base/cmds/idmap2/idmap2d/aidl",
             "frameworks/native/aidl/binder",
             "frameworks/native/cmds/dumpstate/binder",
             "system/core/storaged/binder",
@@ -13,6 +14,7 @@
     srcs: [
         "java/**/*.java",
         ":dumpstate_aidl",
+        ":idmap2_aidl",
         ":netd_aidl",
         ":netd_metrics_aidl",
         ":installd_aidl",
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 807c343..731e6bc 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -16,36 +16,46 @@
 
 package com.android.server.om;
 
+import static android.content.Context.IDMAP_SERVICE;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
 import static com.android.server.om.OverlayManagerService.DEBUG;
 import static com.android.server.om.OverlayManagerService.TAG;
 
 import android.annotation.NonNull;
 import android.content.om.OverlayInfo;
 import android.content.pm.PackageInfo;
+import android.os.IBinder;
+import android.os.IIdmap2;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.Slog;
 
-import com.android.server.pm.Installer.InstallerException;
+import com.android.internal.os.BackgroundThread;
 import com.android.server.pm.Installer;
 
-import java.io.DataInputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
 
 /**
  * Handle the creation and deletion of idmap files.
  *
  * The actual work is performed by the idmap binary, launched through Installer
- * and installd.
+ * and installd (or idmap2).
  *
  * Note: this class is subclassed in the OMS unit tests, and hence not marked as final.
  */
 class IdmapManager {
+    private static final boolean FEATURE_FLAG_IDMAP2 = false;
+
     private final Installer mInstaller;
+    private IIdmap2 mIdmap2Service;
 
     IdmapManager(final Installer installer) {
         mInstaller = installer;
+        if (FEATURE_FLAG_IDMAP2) {
+            connectToIdmap2d();
+        }
     }
 
     boolean createIdmap(@NonNull final PackageInfo targetPackage,
@@ -59,8 +69,12 @@
         final String targetPath = targetPackage.applicationInfo.getBaseCodePath();
         final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
         try {
-            mInstaller.idmap(targetPath, overlayPath, sharedGid);
-        } catch (InstallerException e) {
+            if (FEATURE_FLAG_IDMAP2) {
+                mIdmap2Service.createIdmap(targetPath, overlayPath, userId);
+            } else {
+                mInstaller.idmap(targetPath, overlayPath, sharedGid);
+            }
+        } catch (Exception e) {
             Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
                     + overlayPath + ": " + e.getMessage());
             return false;
@@ -69,13 +83,16 @@
     }
 
     boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
-        // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
         if (DEBUG) {
             Slog.d(TAG, "remove idmap for " + oi.baseCodePath);
         }
         try {
-            mInstaller.removeIdmap(oi.baseCodePath);
-        } catch (InstallerException e) {
+            if (FEATURE_FLAG_IDMAP2) {
+                mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
+            } else {
+                mInstaller.removeIdmap(oi.baseCodePath);
+            }
+        } catch (Exception e) {
             Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
             return false;
         }
@@ -83,19 +100,58 @@
     }
 
     boolean idmapExists(@NonNull final OverlayInfo oi) {
-        // unused OverlayInfo.userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
-        return new File(getIdmapPath(oi.baseCodePath)).isFile();
+        return new File(getIdmapPath(oi.baseCodePath, oi.userId)).isFile();
     }
 
     boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
-        // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
-        return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())).isFile();
+        return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath(), userId))
+            .isFile();
     }
 
-    private String getIdmapPath(@NonNull final String baseCodePath) {
-        final StringBuilder sb = new StringBuilder("/data/resource-cache/");
-        sb.append(baseCodePath.substring(1).replace('/', '@'));
-        sb.append("@idmap");
-        return sb.toString();
+    private @NonNull String getIdmapPath(@NonNull final String overlayPackagePath,
+            final int userId) {
+        if (FEATURE_FLAG_IDMAP2) {
+            try {
+                return mIdmap2Service.getIdmapPath(overlayPackagePath, userId);
+            } catch (Exception e) {
+                Slog.w(TAG, "failed to get idmap path for " + overlayPackagePath + ": "
+                        + e.getMessage());
+                return "";
+            }
+        } else {
+            final StringBuilder sb = new StringBuilder("/data/resource-cache/");
+            sb.append(overlayPackagePath.substring(1).replace('/', '@'));
+            sb.append("@idmap");
+            return sb.toString();
+        }
+    }
+
+    private void connectToIdmap2d() {
+        IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
+        if (binder != null) {
+            try {
+                binder.linkToDeath(new IBinder.DeathRecipient() {
+                    @Override
+                    public void binderDied() {
+                        Slog.w(TAG, "service '" + IDMAP_SERVICE + "' died; reconnecting...");
+                        connectToIdmap2d();
+                    }
+
+                }, 0);
+            } catch (RemoteException e) {
+                binder = null;
+            }
+        }
+        if (binder != null) {
+            mIdmap2Service = IIdmap2.Stub.asInterface(binder);
+            if (DEBUG) {
+                Slog.d(TAG, "service '" + IDMAP_SERVICE + "' connected");
+            }
+        } else {
+            Slog.w(TAG, "service '" + IDMAP_SERVICE + "' not found; trying again...");
+            BackgroundThread.getHandler().postDelayed(() -> {
+                connectToIdmap2d();
+            }, SECOND_IN_MILLIS);
+        }
     }
 }