Merge "Prepare for switch to idmap2"

This commit is contained in:
TreeHugger Robot
2018-11-15 23:37:04 +00:00
committed by Android (Google) Code Review
8 changed files with 191 additions and 32 deletions

View File

@@ -4333,6 +4333,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.

View File

@@ -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> 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.
/**

View File

@@ -24,9 +24,13 @@
#include <sys/system_properties.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
#include <sstream>
#include <string>
#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/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
// use VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> 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<std::string> 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<std::string> 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<std::string> 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},

View File

@@ -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)

View File

@@ -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";

View File

@@ -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/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to
* OVERLAY_DIR.
* APKs in VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
* addition to VENDOR_OVERLAY_DIR.
*/
static const char* OVERLAY_THEME_DIR_PROPERTY;
static const char* TARGET_PACKAGE_NAME;

View File

@@ -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",

View File

@@ -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);
}
}
}