blob: 25a39cc8456fe8bf9e8dcb86f716521fb585de4a [file] [log] [blame]
/*
* Copyright (C) 2016 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.om;
import static com.android.server.om.OverlayManagerService.DEBUG;
import static com.android.server.om.OverlayManagerService.TAG;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.om.OverlayInfo;
import android.content.om.OverlayableInfo;
import android.os.Build.VERSION_CODES;
import android.os.FabricatedOverlayInfo;
import android.os.FabricatedOverlayInternal;
import android.os.OverlayablePolicy;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Slog;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
* Handle the creation and deletion of idmap files.
*
* The actual work is performed by idmap2d.
* @see IdmapDaemon
*/
final class IdmapManager {
private static final boolean VENDOR_IS_Q_OR_LATER;
static {
final String value = SystemProperties.get("ro.vndk.version", "29");
boolean isQOrLater;
try {
isQOrLater = Integer.parseInt(value) >= 29;
} catch (NumberFormatException e) {
// The version is not a number, therefore it is a development codename.
isQOrLater = true;
}
VENDOR_IS_Q_OR_LATER = isQOrLater;
}
static final int IDMAP_NOT_EXIST = 0;
static final int IDMAP_IS_VERIFIED = 1;
static final int IDMAP_IS_MODIFIED = 1 << 1;
@IntDef(flag = true, prefix = { "IDMAP_" }, value = {
IDMAP_NOT_EXIST,
IDMAP_IS_VERIFIED,
IDMAP_IS_MODIFIED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface IdmapStatus {}
private final IdmapDaemon mIdmapDaemon;
private final PackageManagerHelper mPackageManager;
/**
* Package name of the reference package defined in 'overlay-config-signature' tag of
* SystemConfig or empty String if tag not defined. This package is vetted on scan by
* PackageManagerService that it's a system package and is used to check if overlay matches
* its signature in order to fulfill the config_signature policy.
*/
private final String mConfigSignaturePackage;
IdmapManager(final IdmapDaemon idmapDaemon, final PackageManagerHelper packageManager) {
mPackageManager = packageManager;
mIdmapDaemon = idmapDaemon;
mConfigSignaturePackage = packageManager.getConfigSignaturePackage();
}
/**
* Creates the idmap for the target/overlay combination and returns whether the idmap file was
* modified.
* @return the status of the specific idmap file. It's one of the following.<ul>
* <li>{@link #IDMAP_NOT_EXIST} means the idmap file is not existed.</li>
* <li>{@link #IDMAP_IS_VERIFIED} means the idmap file is verified by Idmap2d.</li>
* <li>{@link #IDMAP_IS_MODIFIED | IDMAP_IS_VERIFIED } means the idmap file is modified and
* verified by Idmap2d.</li>
* </ul>.
*/
@IdmapStatus int createIdmap(@NonNull final AndroidPackage targetPackage,
@NonNull PackageState overlayPackageState, @NonNull final AndroidPackage overlayPackage,
String overlayBasePath, String overlayName, @UserIdInt int userId) {
if (DEBUG) {
Slog.d(TAG, "create idmap for " + targetPackage.getPackageName() + " and "
+ overlayPackage.getPackageName());
}
final String targetPath = targetPackage.getSplits().get(0).getPath();
try {
int policies = calculateFulfilledPolicies(targetPackage, overlayPackageState,
overlayPackage, userId);
boolean enforce = enforceOverlayable(overlayPackageState, overlayPackage);
if (mIdmapDaemon.verifyIdmap(targetPath, overlayBasePath, overlayName, policies,
enforce, userId)) {
return IDMAP_IS_VERIFIED;
}
final boolean idmapCreated = mIdmapDaemon.createIdmap(targetPath, overlayBasePath,
overlayName, policies, enforce, userId) != null;
return (idmapCreated) ? IDMAP_IS_MODIFIED | IDMAP_IS_VERIFIED : IDMAP_NOT_EXIST;
} catch (Exception e) {
Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
+ overlayBasePath, e);
return IDMAP_NOT_EXIST;
}
}
boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
if (DEBUG) {
Slog.d(TAG, "remove idmap for " + oi.baseCodePath);
}
try {
return mIdmapDaemon.removeIdmap(oi.baseCodePath, userId);
} catch (Exception e) {
Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath, e);
return false;
}
}
boolean idmapExists(@NonNull final OverlayInfo oi) {
return mIdmapDaemon.idmapExists(oi.baseCodePath, oi.userId);
}
/**
* @return the list of all fabricated overlays
*/
List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
return mIdmapDaemon.getFabricatedOverlayInfos();
}
/**
* Creates a fabricated overlay and persists it to disk.
* @return the path to the fabricated overlay
*/
FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) {
return mIdmapDaemon.createFabricatedOverlay(overlay);
}
/**
* Deletes the fabricated overlay file on disk.
* @return whether the path was deleted
*/
boolean deleteFabricatedOverlay(@NonNull String path) {
return mIdmapDaemon.deleteFabricatedOverlay(path);
}
/**
* Gets the idmap data associated with an overlay, in dump format.
* Only indented for debugging.
*/
String dumpIdmap(@NonNull String overlayPath) {
return mIdmapDaemon.dumpIdmap(overlayPath);
}
/**
* Checks if overlayable and policies should be enforced on the specified overlay for backwards
* compatibility with pre-Q overlays.
*/
private boolean enforceOverlayable(@NonNull PackageState overlayPackageState,
@NonNull final AndroidPackage overlayPackage) {
if (overlayPackage.getTargetSdkVersion() >= VERSION_CODES.Q) {
// Always enforce policies for overlays targeting Q+.
return true;
}
if (overlayPackageState.isVendor()) {
// If the overlay is on a pre-Q vendor partition, do not enforce overlayable
// restrictions on this overlay because the pre-Q platform has no understanding of
// overlayable.
return VENDOR_IS_Q_OR_LATER;
}
// Do not enforce overlayable restrictions on pre-Q overlays that are signed with the
// platform signature or that are preinstalled.
return !(overlayPackageState.isSystem() || overlayPackage.isSignedWithPlatformKey());
}
/**
* Retrieves a bitmask for idmap2 that represents the policies the overlay fulfills.
*/
private int calculateFulfilledPolicies(@NonNull final AndroidPackage targetPackage,
@NonNull PackageState overlayPackageState, @NonNull final AndroidPackage overlayPackage,
@UserIdInt int userId) {
int fulfilledPolicies = OverlayablePolicy.PUBLIC;
// Overlay matches target signature
if (mPackageManager.signaturesMatching(targetPackage.getPackageName(),
overlayPackage.getPackageName(), userId)) {
fulfilledPolicies |= OverlayablePolicy.SIGNATURE;
}
// Overlay matches actor signature
if (matchesActorSignature(targetPackage, overlayPackage, userId)) {
fulfilledPolicies |= OverlayablePolicy.ACTOR_SIGNATURE;
}
// If SystemConfig defines 'overlay-config-signature' package, given that
// this package is vetted by OverlayManagerService that it's a
// preinstalled package, check if overlay matches its signature.
if (!TextUtils.isEmpty(mConfigSignaturePackage)
&& mPackageManager.signaturesMatching(mConfigSignaturePackage,
overlayPackage.getPackageName(),
userId)) {
fulfilledPolicies |= OverlayablePolicy.CONFIG_SIGNATURE;
}
// Vendor partition (/vendor)
if (overlayPackageState.isVendor()) {
return fulfilledPolicies | OverlayablePolicy.VENDOR_PARTITION;
}
// Product partition (/product)
if (overlayPackageState.isProduct()) {
return fulfilledPolicies | OverlayablePolicy.PRODUCT_PARTITION;
}
// Odm partition (/odm)
if (overlayPackageState.isOdm()) {
return fulfilledPolicies | OverlayablePolicy.ODM_PARTITION;
}
// Oem partition (/oem)
if (overlayPackageState.isOem()) {
return fulfilledPolicies | OverlayablePolicy.OEM_PARTITION;
}
// System_ext partition (/system_ext) is considered as system
// Check this last since every partition except for data is scanned as system in the PMS.
if (overlayPackageState.isSystem() || overlayPackageState.isSystemExt()) {
return fulfilledPolicies | OverlayablePolicy.SYSTEM_PARTITION;
}
return fulfilledPolicies;
}
private boolean matchesActorSignature(@NonNull AndroidPackage targetPackage,
@NonNull AndroidPackage overlayPackage, int userId) {
String targetOverlayableName = overlayPackage.getOverlayTargetOverlayableName();
if (targetOverlayableName != null) {
try {
OverlayableInfo overlayableInfo = mPackageManager.getOverlayableForTarget(
targetPackage.getPackageName(), targetOverlayableName, userId);
if (overlayableInfo != null && overlayableInfo.actor != null) {
String actorPackageName = OverlayActorEnforcer.getPackageNameForActor(
overlayableInfo.actor, mPackageManager.getNamedActors()).first;
if (mPackageManager.signaturesMatching(actorPackageName,
overlayPackage.getPackageName(), userId)) {
return true;
}
}
} catch (IOException ignored) {
}
}
return false;
}
}