From 06a1ac82294c2f6b73af59936195546b5eb78ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Thu, 20 Sep 2018 13:09:47 +0200 Subject: [PATCH] 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 --- core/java/android/content/Context.java | 10 ++ .../android/content/res/AssetManager.java | 18 +++- core/jni/android_util_AssetManager.cpp | 87 ++++++++++++++++- core/jni/fd_utils.cpp | 4 +- libs/androidfw/AssetManager.cpp | 2 +- .../include/androidfw/AssetManager.h | 6 +- services/core/Android.bp | 2 + .../com/android/server/om/IdmapManager.java | 94 +++++++++++++++---- 8 files changed, 191 insertions(+), 32 deletions(-) diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 2aa32c4b9cff6..61d004e00b4a1 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4305,6 +4305,16 @@ public abstract class Context { */ public static final String OVERLAY_SERVICE = "overlay"; + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {android.os.IIdmap2} for managing idmap files (used by overlay + * packages). + * + * @see #getSystemService(String) + * @hide + */ + public static final String IDMAP_SERVICE = "idmap"; + /** * Use with {@link #getSystemService(String)} to retrieve a * {@link VrManager} for accessing the VR service. diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 5f23749957753..4371c772c047c 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -58,6 +58,7 @@ import java.util.HashMap; public final class AssetManager implements AutoCloseable { private static final String TAG = "AssetManager"; private static final boolean DEBUG_REFS = false; + private static final boolean FEATURE_FLAG_IDMAP2 = false; private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; @@ -195,13 +196,23 @@ public final class AssetManager implements AutoCloseable { return; } - // Make sure that all IDMAPs are up to date. - nativeVerifySystemIdmaps(); try { final ArrayList apkAssets = new ArrayList<>(); apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); - loadStaticRuntimeOverlays(apkAssets); + if (FEATURE_FLAG_IDMAP2) { + final String[] systemIdmapPaths = + nativeCreateIdmapsForStaticOverlaysTargetingAndroid(); + if (systemIdmapPaths == null) { + throw new IOException("idmap2 scan failed"); + } + for (String idmapPath : systemIdmapPaths) { + apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); + } + } else { + nativeVerifySystemIdmaps(); + loadStaticRuntimeOverlays(apkAssets); + } sSystemApkAssetsSet = new ArraySet<>(apkAssets); sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); @@ -1404,6 +1415,7 @@ public final class AssetManager implements AutoCloseable { private static native long nativeAssetGetRemainingLength(long assetPtr); private static native void nativeVerifySystemIdmaps(); + private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid(); // Global debug native methods. /** diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index b2d44e73d8619..7b564ae162ce6 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -24,9 +24,13 @@ #include #include #include +#include #include // for AID_SYSTEM +#include +#include + #include "android-base/logging.h" #include "android-base/properties.h" #include "android-base/stringprintf.h" @@ -38,6 +42,7 @@ #include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" #include "androidfw/MutexGuard.h" +#include "androidfw/PosixUtils.h" #include "androidfw/ResourceTypes.h" #include "core_jni_helpers.h" #include "jni.h" @@ -54,6 +59,7 @@ extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); using ::android::base::StringPrintf; +using ::android::util::ExecuteBinary; namespace android { @@ -161,18 +167,20 @@ static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { argv[argc++] = AssetManager::IDMAP_DIR; // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + // use VENDOR_OVERLAY_DIR/ in + // addition to VENDOR_OVERLAY_DIR. std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, ""); if (!overlay_theme_path.empty()) { - overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; + overlay_theme_path = + std::string(AssetManager::VENDOR_OVERLAY_DIR) + "/" + overlay_theme_path; if (stat(overlay_theme_path.c_str(), &st) == 0) { argv[argc++] = overlay_theme_path.c_str(); } } - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; + if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::VENDOR_OVERLAY_DIR; } if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { @@ -200,6 +208,75 @@ static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { } } +static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env, + jclass /*clazz*/) { + // --input-directory can be given multiple times, but idmap2 expects the directory to exist + std::vector input_dirs; + struct stat st; + if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) { + input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR); + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR); + } + + if (stat(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR, &st) == 0) { + input_dirs.push_back(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR); + } + + if (input_dirs.empty()) { + LOG(WARNING) << "no directories for idmap2 to scan"; + return env->NewObjectArray(0, g_stringClass, nullptr); + } + + std::vector argv{"/system/bin/idmap2", + "scan", + "--recursive", + "--target-package-name", "android", + "--target-apk-path", "/system/framework/framework-res.apk", + "--output-directory", "/data/resource-cache"}; + + for (const auto& dir : input_dirs) { + argv.push_back("--input-directory"); + argv.push_back(dir); + } + + const auto result = ExecuteBinary(argv); + + if (!result) { + LOG(ERROR) << "failed to execute idmap2"; + return nullptr; + } + + if (result->status != 0) { + LOG(ERROR) << "idmap2: " << result->stderr; + return nullptr; + } + + std::vector idmap_paths; + std::istringstream input(result->stdout); + std::string path; + while (std::getline(input, path)) { + idmap_paths.push_back(path); + } + + jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + for (size_t i = 0; i < idmap_paths.size(); i++) { + const std::string path = idmap_paths[i]; + jstring java_string = env->NewStringUTF(path.c_str()); + if (env->ExceptionCheck()) { + return nullptr; + } + env->SetObjectArrayElement(array, i, java_string); + env->DeleteLocalRef(java_string); + } + return array; +} + static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); @@ -1405,6 +1482,8 @@ static const JNINativeMethod gAssetManagerMethods[] = { // System/idmap related methods. {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, + {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;", + (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid}, // Global management/debug methods. {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 2465759a9fa5c..a398e498a3011 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -85,7 +85,7 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { // See AssetManager.cpp for more details on overlay-subdir. static const char* kOverlayDir = "/system/vendor/overlay/"; static const char* kVendorOverlayDir = "/vendor/overlay"; - static const char* kOverlaySubdir = "/system/vendor/overlay-subdir/"; + static const char* kVendorOverlaySubdir = "/system/vendor/overlay-subdir/"; static const char* kSystemProductOverlayDir = "/system/product/overlay/"; static const char* kProductOverlayDir = "/product/overlay"; static const char* kSystemProductServicesOverlayDir = "/system/product_services/overlay/"; @@ -93,7 +93,7 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { static const char* kApkSuffix = ".apk"; if ((android::base::StartsWith(path, kOverlayDir) - || android::base::StartsWith(path, kOverlaySubdir) + || android::base::StartsWith(path, kVendorOverlaySubdir) || android::base::StartsWith(path, kVendorOverlayDir) || android::base::StartsWith(path, kSystemProductOverlayDir) || android::base::StartsWith(path, kProductOverlayDir) diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 843c1461e21be..1cb0d25d8c080 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -72,7 +72,7 @@ static volatile int32_t gCount = 0; const char* AssetManager::RESOURCES_FILENAME = "resources.arsc"; const char* AssetManager::IDMAP_BIN = "/system/bin/idmap"; -const char* AssetManager::OVERLAY_DIR = "/vendor/overlay"; +const char* AssetManager::VENDOR_OVERLAY_DIR = "/vendor/overlay"; const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay"; const char* AssetManager::PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay"; const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme"; diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h index cdb87bcb8e114..e22e2d239a55f 100644 --- a/libs/androidfw/include/androidfw/AssetManager.h +++ b/libs/androidfw/include/androidfw/AssetManager.h @@ -59,13 +59,13 @@ class AssetManager : public AAssetManager { public: static const char* RESOURCES_FILENAME; static const char* IDMAP_BIN; - static const char* OVERLAY_DIR; + static const char* VENDOR_OVERLAY_DIR; static const char* PRODUCT_OVERLAY_DIR; static const char* PRODUCT_SERVICES_OVERLAY_DIR; /* * If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay - * APKs in OVERLAY_DIR/ in addition to - * OVERLAY_DIR. + * APKs in VENDOR_OVERLAY_DIR/ in + * addition to VENDOR_OVERLAY_DIR. */ static const char* OVERLAY_THEME_DIR_PROPERTY; static const char* TARGET_PACKAGE_NAME; diff --git a/services/core/Android.bp b/services/core/Android.bp index 888ad1da3fcc3..617430090998e 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -3,6 +3,7 @@ java_library_static { 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 @@ java_library_static { 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 807c343d0d10a..731e6bcfb0740 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 @@ class IdmapManager { 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 @@ class IdmapManager { } 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 @@ class IdmapManager { } 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); + } } }