All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.SystemConfig Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * Copyright (C) 2014 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;

import static com.android.internal.util.ArrayUtils.appendInt;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.CarrierAssociatedAppEntry;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Process;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.VintfRuntimeInfo;
import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.permission.PermissionManager.SplitPermissionInfo;
import android.sysprop.ApexProperties;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimingsTraceLog;
import android.util.Xml;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.build.UnboundedSdkLevel;

import libcore.io.IoUtils;
import libcore.util.EmptyArray;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Loads global system configuration info.
 * Note: Initializing this class hits the disk and is slow.  This class should generally only be
 * accessed by the system_server process.
 */
public class SystemConfig {
    static final String TAG = "SystemConfig";

    static SystemConfig sInstance;

    // permission flag, determines which types of configuration are allowed to be read
    private static final int ALLOW_FEATURES = 0x001;
    private static final int ALLOW_LIBS = 0x002;
    private static final int ALLOW_PERMISSIONS = 0x004;
    private static final int ALLOW_APP_CONFIGS = 0x008;
    private static final int ALLOW_PRIVAPP_PERMISSIONS = 0x010;
    private static final int ALLOW_OEM_PERMISSIONS = 0x020;
    private static final int ALLOW_HIDDENAPI_WHITELISTING = 0x040;
    private static final int ALLOW_ASSOCIATIONS = 0x080;
    // ALLOW_OVERRIDE_APP_RESTRICTIONS allows to use "allow-in-power-save-except-idle",
    // "allow-in-power-save", "allow-in-data-usage-save","allow-unthrottled-location",
    // "allow-ignore-location-settings" and "allow-adas-location-settings".
    private static final int ALLOW_OVERRIDE_APP_RESTRICTIONS = 0x100;
    private static final int ALLOW_IMPLICIT_BROADCASTS = 0x200;
    private static final int ALLOW_VENDOR_APEX = 0x400;
    private static final int ALLOW_ALL = ~0;

    // property for runtime configuration differentiation
    private static final String SKU_PROPERTY = "ro.boot.product.hardware.sku";

    // property for runtime configuration differentiation in vendor
    private static final String VENDOR_SKU_PROPERTY = "ro.boot.product.vendor.sku";

    private static final ArrayMap> EMPTY_PERMISSIONS =
            new ArrayMap<>();

    // Group-ids that are given to all packages as read from etc/permissions/*.xml.
    int[] mGlobalGids = EmptyArray.INT;

    // These are the built-in uid -> permission mappings that were read from the
    // system configuration files.
    final SparseArray> mSystemPermissions = new SparseArray<>();

    final ArrayList mSplitPermissions = new ArrayList<>();

    private static boolean isAtLeastSdkLevel(String version) {
        try {
            return UnboundedSdkLevel.isAtLeast(version);
        } catch (IllegalArgumentException e) {
            // UnboundedSdkLevel throws when it sees a known old codename
            return false;
        }
    }

    private static boolean isAtMostSdkLevel(String version) {
        try {
            return UnboundedSdkLevel.isAtMost(version);
        } catch (IllegalArgumentException e) {
            // UnboundedSdkLevel throws when it sees a known old codename
            return true;
        }
    }

    public static final class SharedLibraryEntry {
        public final String name;
        public final String filename;
        public final String[] dependencies;

        /**
         * SDK version this library was added to the BOOTCLASSPATH.
         *
         * 

At the SDK level specified in this field and higher, the apps' uses-library tags for * this library will be ignored, since the library is always available on BOOTCLASSPATH. * *

0 means not specified. */ public final String onBootclasspathSince; /** * SDK version this library was removed from the BOOTCLASSPATH. * *

At the SDK level specified in this field and higher, this library needs to be * explicitly added by apps. For compatibility reasons, when an app * targets an SDK less than the value of this attribute, this library is automatically * added. * *

0 means not specified. */ public final String onBootclasspathBefore; /** * Declares whether this library can be safely ignored from tags. * *

This can happen if the library initially had to be explicitly depended-on using that * tag but has since been moved to the BOOTCLASSPATH which means now is always available * and the tag is no longer required. */ public final boolean canBeSafelyIgnored; public final boolean isNative; @VisibleForTesting public SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) { this(name, filename, dependencies, null /* onBootclasspathSince */, null /* onBootclasspathBefore */, isNative); } @VisibleForTesting public SharedLibraryEntry(String name, String filename, String[] dependencies, String onBootclasspathSince, String onBootclasspathBefore) { this(name, filename, dependencies, onBootclasspathSince, onBootclasspathBefore, false /* isNative */); } SharedLibraryEntry(String name, String filename, String[] dependencies, String onBootclasspathSince, String onBootclasspathBefore, boolean isNative) { this.name = name; this.filename = filename; this.dependencies = dependencies; this.onBootclasspathSince = onBootclasspathSince; this.onBootclasspathBefore = onBootclasspathBefore; this.isNative = isNative; // this entry can be ignored if either: // - onBootclasspathSince is set and we are at or past that SDK // - onBootclasspathBefore is set and we are before that SDK canBeSafelyIgnored = (this.onBootclasspathSince != null && isAtLeastSdkLevel(this.onBootclasspathSince)) || (this.onBootclasspathBefore != null && !isAtLeastSdkLevel(this.onBootclasspathBefore)); } } // These are the built-in shared libraries that were read from the // system configuration files. Keys are the library names; values are // the individual entries that contain information such as filename // and dependencies. final ArrayMap mSharedLibraries = new ArrayMap<>(); // These are the features this devices supports that were read from the // system configuration files. final ArrayMap mAvailableFeatures = new ArrayMap<>(); // These are the features which this device doesn't support; the OEM // partition uses these to opt-out of features from the system image. final ArraySet mUnavailableFeatures = new ArraySet<>(); public static final class PermissionEntry { public final String name; public int[] gids; public boolean perUser; PermissionEntry(String name, boolean perUser) { this.name = name; this.perUser = perUser; } } // These are the permission -> gid mappings that were read from the // system configuration files. final ArrayMap mPermissions = new ArrayMap<>(); // These are the packages that are white-listed to be able to run in the // background while in power save mode (but not whitelisted from device idle modes), // as read from the configuration files. final ArraySet mAllowInPowerSaveExceptIdle = new ArraySet<>(); // These are the packages that are white-listed to be able to run in the // background while in power save mode, as read from the configuration files. final ArraySet mAllowInPowerSave = new ArraySet<>(); // These are the packages that are white-listed to be able to run in the // background while in data-usage save mode, as read from the configuration files. final ArraySet mAllowInDataUsageSave = new ArraySet<>(); // These are the packages that are white-listed to be able to run background location // without throttling, as read from the configuration files. final ArraySet mAllowUnthrottledLocation = new ArraySet<>(); // These are the packages that are allow-listed to be able to retrieve location when // the location state is driver assistance only. final ArrayMap> mAllowAdasSettings = new ArrayMap<>(); // These are the packages that are white-listed to be able to retrieve location even when user // location settings are off, for emergency purposes, as read from the configuration files. final ArrayMap> mAllowIgnoreLocationSettings = new ArrayMap<>(); // These are the action strings of broadcasts which are whitelisted to // be delivered anonymously even to apps which target O+. final ArraySet mAllowImplicitBroadcasts = new ArraySet<>(); // These are the packages that are exempted from the background restriction applied // by the system automatically, i.e., due to high background current drain. final ArraySet mBgRestrictionExemption = new ArraySet<>(); // These are the package names of apps which should be automatically granted domain verification // for all of their domains. The only way these apps can be overridden by the user is by // explicitly disabling overall link handling support in app info. final ArraySet mLinkedApps = new ArraySet<>(); // These are the components that are enabled by default as VR mode listener services. final ArraySet mDefaultVrComponents = new ArraySet<>(); // These are the permitted backup transport service components final ArraySet mBackupTransportWhitelist = new ArraySet<>(); // These are packages mapped to maps of component class name to default enabled state. final ArrayMap> mPackageComponentEnabledState = new ArrayMap<>(); // Package names that are exempted from private API blacklisting final ArraySet mHiddenApiPackageWhitelist = new ArraySet<>(); // The list of carrier applications which should be disabled until used. // This function suppresses update notifications for these pre-installed apps. // In SubscriptionInfoUpdater, the listed applications are disabled until used when all of the // following conditions are met. // 1. Not currently carrier-privileged according to the inserted SIM // 2. Pre-installed // 3. In the default state (enabled but not explicitly) // And SubscriptionInfoUpdater undoes this and marks the app enabled when a SIM is inserted // that marks the app as carrier privileged. It also grants the app default permissions // for Phone and Location. As such, apps MUST only ever be added to this list if they // obtain user consent to access their location through other means. final ArraySet mDisabledUntilUsedPreinstalledCarrierApps = new ArraySet<>(); // These are the packages of carrier-associated apps which should be disabled until used until // a SIM is inserted which grants carrier privileges to that carrier app. final ArrayMap> mDisabledUntilUsedPreinstalledCarrierAssociatedApps = new ArrayMap<>(); final ArrayMap> mPrivAppPermissions = new ArrayMap<>(); final ArrayMap> mPrivAppDenyPermissions = new ArrayMap<>(); final ArrayMap> mVendorPrivAppPermissions = new ArrayMap<>(); final ArrayMap> mVendorPrivAppDenyPermissions = new ArrayMap<>(); final ArrayMap> mProductPrivAppPermissions = new ArrayMap<>(); final ArrayMap> mProductPrivAppDenyPermissions = new ArrayMap<>(); final ArrayMap> mSystemExtPrivAppPermissions = new ArrayMap<>(); final ArrayMap> mSystemExtPrivAppDenyPermissions = new ArrayMap<>(); final ArrayMap>> mApexPrivAppPermissions = new ArrayMap<>(); final ArrayMap>> mApexPrivAppDenyPermissions = new ArrayMap<>(); final ArrayMap> mOemPermissions = new ArrayMap<>(); // Allowed associations between applications. If there are any entries // for an app, those are the only associations allowed; otherwise, all associations // are allowed. Allowing an association from app A to app B means app A can not // associate with any other apps, but does not limit what apps B can associate with. final ArrayMap> mAllowedAssociations = new ArrayMap<>(); private final ArraySet mBugreportWhitelistedPackages = new ArraySet<>(); private final ArraySet mAppDataIsolationWhitelistedApps = new ArraySet<>(); // Map of packagesNames to userTypes. Stored temporarily until cleared by UserManagerService(). private ArrayMap> mPackageToUserTypeWhitelist = new ArrayMap<>(); private ArrayMap> mPackageToUserTypeBlacklist = new ArrayMap<>(); private final ArraySet mRollbackWhitelistedPackages = new ArraySet<>(); private final ArraySet mWhitelistedStagedInstallers = new ArraySet<>(); // A map from package name of vendor APEXes that can be updated to an installer package name // allowed to install updates for it. private final ArrayMap mAllowedVendorApexes = new ArrayMap<>(); private String mModulesInstallerPackageName; /** * Map of system pre-defined, uniquely named actors; keys are namespace, * value maps actor name to package name. */ private Map> mNamedActors = null; // Package name of the package pre-installed on a read-only // partition that is used to verify if an overlay package fulfills // the 'config_signature' policy by comparing their signatures: // if the overlay package is signed with the same certificate as // the package declared in 'overlay-config-signature' tag, then the // overlay package fulfills the 'config_signature' policy. private String mOverlayConfigSignaturePackage; public static SystemConfig getInstance() { if (!isSystemProcess()) { Slog.wtf(TAG, "SystemConfig is being accessed by a process other than " + "system_server."); } synchronized (SystemConfig.class) { if (sInstance == null) { sInstance = new SystemConfig(); } return sInstance; } } public int[] getGlobalGids() { return mGlobalGids; } public SparseArray> getSystemPermissions() { return mSystemPermissions; } public ArrayList getSplitPermissions() { return mSplitPermissions; } public ArrayMap getSharedLibraries() { return mSharedLibraries; } public ArrayMap getAvailableFeatures() { return mAvailableFeatures; } public ArrayMap getPermissions() { return mPermissions; } public ArraySet getAllowImplicitBroadcasts() { return mAllowImplicitBroadcasts; } public ArraySet getAllowInPowerSaveExceptIdle() { return mAllowInPowerSaveExceptIdle; } public ArraySet getAllowInPowerSave() { return mAllowInPowerSave; } public ArraySet getAllowInDataUsageSave() { return mAllowInDataUsageSave; } public ArraySet getAllowUnthrottledLocation() { return mAllowUnthrottledLocation; } public ArrayMap> getAllowAdasLocationSettings() { return mAllowAdasSettings; } public ArrayMap> getAllowIgnoreLocationSettings() { return mAllowIgnoreLocationSettings; } public ArraySet getBgRestrictionExemption() { return mBgRestrictionExemption; } public ArraySet getLinkedApps() { return mLinkedApps; } public ArraySet getHiddenApiWhitelistedApps() { return mHiddenApiPackageWhitelist; } public ArraySet getDefaultVrComponents() { return mDefaultVrComponents; } public ArraySet getBackupTransportWhitelist() { return mBackupTransportWhitelist; } public ArrayMap getComponentsEnabledStates(String packageName) { return mPackageComponentEnabledState.get(packageName); } public ArraySet getDisabledUntilUsedPreinstalledCarrierApps() { return mDisabledUntilUsedPreinstalledCarrierApps; } public ArrayMap> getDisabledUntilUsedPreinstalledCarrierAssociatedApps() { return mDisabledUntilUsedPreinstalledCarrierAssociatedApps; } public ArraySet getPrivAppPermissions(String packageName) { return mPrivAppPermissions.get(packageName); } public ArraySet getPrivAppDenyPermissions(String packageName) { return mPrivAppDenyPermissions.get(packageName); } /** Get privapp permission allowlist for an apk-in-apex. */ public ArraySet getApexPrivAppPermissions(String apexName, String apkPackageName) { return mApexPrivAppPermissions.getOrDefault(apexName, EMPTY_PERMISSIONS) .get(apkPackageName); } /** Get privapp permissions denylist for an apk-in-apex. */ public ArraySet getApexPrivAppDenyPermissions(String apexName, String apkPackageName) { return mApexPrivAppDenyPermissions.getOrDefault(apexName, EMPTY_PERMISSIONS) .get(apkPackageName); } public ArraySet getVendorPrivAppPermissions(String packageName) { return mVendorPrivAppPermissions.get(packageName); } public ArraySet getVendorPrivAppDenyPermissions(String packageName) { return mVendorPrivAppDenyPermissions.get(packageName); } public ArraySet getProductPrivAppPermissions(String packageName) { return mProductPrivAppPermissions.get(packageName); } public ArraySet getProductPrivAppDenyPermissions(String packageName) { return mProductPrivAppDenyPermissions.get(packageName); } /** * Read from "permission" tags in /system_ext/etc/permissions/*.xml * @return Set of privileged permissions that are explicitly granted. */ public ArraySet getSystemExtPrivAppPermissions(String packageName) { return mSystemExtPrivAppPermissions.get(packageName); } /** * Read from "deny-permission" tags in /system_ext/etc/permissions/*.xml * @return Set of privileged permissions that are explicitly denied. */ public ArraySet getSystemExtPrivAppDenyPermissions(String packageName) { return mSystemExtPrivAppDenyPermissions.get(packageName); } public Map getOemPermissions(String packageName) { final Map oemPermissions = mOemPermissions.get(packageName); if (oemPermissions != null) { return oemPermissions; } return Collections.emptyMap(); } public ArrayMap> getAllowedAssociations() { return mAllowedAssociations; } public ArraySet getBugreportWhitelistedPackages() { return mBugreportWhitelistedPackages; } public Set getRollbackWhitelistedPackages() { return mRollbackWhitelistedPackages; } public Set getWhitelistedStagedInstallers() { return mWhitelistedStagedInstallers; } public Map getAllowedVendorApexes() { return mAllowedVendorApexes; } public String getModulesInstallerPackageName() { return mModulesInstallerPackageName; } public ArraySet getAppDataIsolationWhitelistedApps() { return mAppDataIsolationWhitelistedApps; } /** * Gets map of packagesNames to userTypes, dictating on which user types each package should be * initially installed, and then removes this map from SystemConfig. * Called by UserManagerService when it is constructed. */ public ArrayMap> getAndClearPackageToUserTypeWhitelist() { ArrayMap> r = mPackageToUserTypeWhitelist; mPackageToUserTypeWhitelist = new ArrayMap<>(0); return r; } /** * Gets map of packagesNames to userTypes, dictating on which user types each package should NOT * be initially installed, even if they are whitelisted, and then removes this map from * SystemConfig. * Called by UserManagerService when it is constructed. */ public ArrayMap> getAndClearPackageToUserTypeBlacklist() { ArrayMap> r = mPackageToUserTypeBlacklist; mPackageToUserTypeBlacklist = new ArrayMap<>(0); return r; } @NonNull public Map> getNamedActors() { return mNamedActors != null ? mNamedActors : Collections.emptyMap(); } @Nullable public String getOverlayConfigSignaturePackage() { return TextUtils.isEmpty(mOverlayConfigSignaturePackage) ? null : mOverlayConfigSignaturePackage; } /** * Only use for testing. Do NOT use in production code. * @param readPermissions false to create an empty SystemConfig; true to read the permissions. */ @VisibleForTesting public SystemConfig(boolean readPermissions) { if (readPermissions) { Slog.w(TAG, "Constructing a test SystemConfig"); readAllPermissions(); } else { Slog.w(TAG, "Constructing an empty test SystemConfig"); } } SystemConfig() { TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER); log.traceBegin("readAllPermissions"); try { readAllPermissions(); readPublicNativeLibrariesList(); } finally { log.traceEnd(); } } private void readAllPermissions() { final XmlPullParser parser = Xml.newPullParser(); // Read configuration from system readPermissions(parser, Environment.buildPath( Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL); // Read configuration from the old permissions dir readPermissions(parser, Environment.buildPath( Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL); // Vendors are only allowed to customize these int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS | ALLOW_ASSOCIATIONS | ALLOW_VENDOR_APEX; if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.O_MR1) { // For backward compatibility vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS); } readPermissions(parser, Environment.buildPath( Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag); readPermissions(parser, Environment.buildPath( Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag); String vendorSkuProperty = SystemProperties.get(VENDOR_SKU_PROPERTY, ""); if (!vendorSkuProperty.isEmpty()) { String vendorSkuDir = "sku_" + vendorSkuProperty; readPermissions(parser, Environment.buildPath( Environment.getVendorDirectory(), "etc", "sysconfig", vendorSkuDir), vendorPermissionFlag); readPermissions(parser, Environment.buildPath( Environment.getVendorDirectory(), "etc", "permissions", vendorSkuDir), vendorPermissionFlag); } // Allow ODM to customize system configs as much as Vendor, because /odm is another // vendor partition other than /vendor. int odmPermissionFlag = vendorPermissionFlag; readPermissions(parser, Environment.buildPath( Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag); readPermissions(parser, Environment.buildPath( Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag); String skuProperty = SystemProperties.get(SKU_PROPERTY, ""); if (!skuProperty.isEmpty()) { String skuDir = "sku_" + skuProperty; readPermissions(parser, Environment.buildPath( Environment.getOdmDirectory(), "etc", "sysconfig", skuDir), odmPermissionFlag); readPermissions(parser, Environment.buildPath( Environment.getOdmDirectory(), "etc", "permissions", skuDir), odmPermissionFlag); } // Allow OEM to customize these int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS | ALLOW_VENDOR_APEX; readPermissions(parser, Environment.buildPath( Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag); readPermissions(parser, Environment.buildPath( Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag); // Allow Product to customize these configs // TODO(b/157203468): ALLOW_HIDDENAPI_WHITELISTING must be removed because we prohibited // the use of hidden APIs from the product partition. int productPermissionFlag = ALLOW_FEATURES | ALLOW_LIBS | ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS | ALLOW_HIDDENAPI_WHITELISTING | ALLOW_ASSOCIATIONS | ALLOW_OVERRIDE_APP_RESTRICTIONS | ALLOW_IMPLICIT_BROADCASTS | ALLOW_VENDOR_APEX; if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.R) { // TODO(b/157393157): This must check product interface enforcement instead of // DEVICE_INITIAL_SDK_INT for the devices without product interface enforcement. productPermissionFlag = ALLOW_ALL; } readPermissions(parser, Environment.buildPath( Environment.getProductDirectory(), "etc", "sysconfig"), productPermissionFlag); readPermissions(parser, Environment.buildPath( Environment.getProductDirectory(), "etc", "permissions"), productPermissionFlag); // Allow /system_ext to customize all system configs readPermissions(parser, Environment.buildPath( Environment.getSystemExtDirectory(), "etc", "sysconfig"), ALLOW_ALL); readPermissions(parser, Environment.buildPath( Environment.getSystemExtDirectory(), "etc", "permissions"), ALLOW_ALL); // Skip loading configuration from apex if it is not a system process. if (!isSystemProcess()) { return; } // Read configuration of features, libs and priv-app permissions from apex module. int apexPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS; // TODO: Use a solid way to filter apex module folders? for (File f: FileUtils.listFilesOrEmpty(Environment.getApexDirectory())) { if (f.isFile() || f.getPath().contains("@")) { continue; } readPermissions(parser, Environment.buildPath(f, "etc", "permissions"), apexPermissionFlag); } } @VisibleForTesting public void readPermissions(final XmlPullParser parser, File libraryDir, int permissionFlag) { // Read permissions from given directory. if (!libraryDir.exists() || !libraryDir.isDirectory()) { if (permissionFlag == ALLOW_ALL) { Slog.w(TAG, "No directory " + libraryDir + ", skipping"); } return; } if (!libraryDir.canRead()) { Slog.w(TAG, "Directory " + libraryDir + " cannot be read"); return; } // Iterate over the files in the directory and scan .xml files File platformFile = null; for (File f : libraryDir.listFiles()) { if (!f.isFile()) { continue; } // We'll read platform.xml last if (f.getPath().endsWith("etc/permissions/platform.xml")) { platformFile = f; continue; } if (!f.getPath().endsWith(".xml")) { Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); continue; } if (!f.canRead()) { Slog.w(TAG, "Permissions library file " + f + " cannot be read"); continue; } readPermissionsFromXml(parser, f, permissionFlag); } // Read platform permissions last so it will take precedence if (platformFile != null) { readPermissionsFromXml(parser, platformFile, permissionFlag); } } private void logNotAllowedInPartition(String name, File permFile, XmlPullParser parser) { Slog.w(TAG, "<" + name + "> not allowed in partition of " + permFile + " at " + parser.getPositionDescription()); } private void readPermissionsFromXml(final XmlPullParser parser, File permFile, int permissionFlag) { final FileReader permReader; try { permReader = new FileReader(permFile); } catch (FileNotFoundException e) { Slog.w(TAG, "Couldn't find or open permissions file " + permFile); return; } Slog.i(TAG, "Reading permissions from " + permFile); final boolean lowRam = ActivityManager.isLowRamDeviceStatic(); try { parser.setInput(permReader); int type; while ((type=parser.next()) != parser.START_TAG && type != parser.END_DOCUMENT) { ; } if (type != parser.START_TAG) { throw new XmlPullParserException("No start tag found"); } if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) { throw new XmlPullParserException("Unexpected start tag in " + permFile + ": found " + parser.getName() + ", expected 'permissions' or 'config'"); } final boolean allowAll = permissionFlag == ALLOW_ALL; final boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0; final boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0; final boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0; final boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0; final boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0; final boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0; final boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING) != 0; final boolean allowAssociations = (permissionFlag & ALLOW_ASSOCIATIONS) != 0; final boolean allowOverrideAppRestrictions = (permissionFlag & ALLOW_OVERRIDE_APP_RESTRICTIONS) != 0; final boolean allowImplicitBroadcasts = (permissionFlag & ALLOW_IMPLICIT_BROADCASTS) != 0; final boolean allowVendorApex = (permissionFlag & ALLOW_VENDOR_APEX) != 0; while (true) { XmlUtils.nextElement(parser); if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { break; } String name = parser.getName(); if (name == null) { XmlUtils.skipCurrentTag(parser); continue; } switch (name) { case "group": { if (allowAll) { String gidStr = parser.getAttributeValue(null, "gid"); if (gidStr != null) { int gid = android.os.Process.getGidForName(gidStr); mGlobalGids = appendInt(mGlobalGids, gid); } else { Slog.w(TAG, "<" + name + "> without gid in " + permFile + " at " + parser.getPositionDescription()); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "permission": { if (allowPermissions) { String perm = parser.getAttributeValue(null, "name"); if (perm == null) { Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); break; } perm = perm.intern(); readPermission(parser, perm); } else { logNotAllowedInPartition(name, permFile, parser); XmlUtils.skipCurrentTag(parser); } } break; case "assign-permission": { if (allowPermissions) { String perm = parser.getAttributeValue(null, "name"); if (perm == null) { Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); break; } String uidStr = parser.getAttributeValue(null, "uid"); if (uidStr == null) { Slog.w(TAG, "<" + name + "> without uid in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); break; } int uid = Process.getUidForName(uidStr); if (uid < 0) { Slog.w(TAG, "<" + name + "> with unknown uid \"" + uidStr + " in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); break; } perm = perm.intern(); ArraySet perms = mSystemPermissions.get(uid); if (perms == null) { perms = new ArraySet(); mSystemPermissions.put(uid, perms); } perms.add(perm); } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "split-permission": { if (allowPermissions) { readSplitPermission(parser, permFile); } else { logNotAllowedInPartition(name, permFile, parser); XmlUtils.skipCurrentTag(parser); } } break; case "apex-library": // "apex-library" is meant to behave exactly like "library" case "library": { if (allowLibs) { String lname = parser.getAttributeValue(null, "name"); String lfile = parser.getAttributeValue(null, "file"); String ldependency = parser.getAttributeValue(null, "dependency"); String minDeviceSdk = parser.getAttributeValue(null, "min-device-sdk"); String maxDeviceSdk = parser.getAttributeValue(null, "max-device-sdk"); if (lname == null) { Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " + parser.getPositionDescription()); } else if (lfile == null) { Slog.w(TAG, "<" + name + "> without file in " + permFile + " at " + parser.getPositionDescription()); } else { boolean allowedMinSdk = minDeviceSdk == null || isAtLeastSdkLevel(minDeviceSdk); boolean allowedMaxSdk = maxDeviceSdk == null || isAtMostSdkLevel(maxDeviceSdk); final boolean exists = new File(lfile).exists(); if (allowedMinSdk && allowedMaxSdk && exists) { String bcpSince = parser.getAttributeValue(null, "on-bootclasspath-since"); String bcpBefore = parser.getAttributeValue(null, "on-bootclasspath-before"); SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile, ldependency == null ? new String[0] : ldependency.split(":"), bcpSince, bcpBefore); mSharedLibraries.put(lname, entry); } else { final StringBuilder msg = new StringBuilder( "Ignore shared library ").append(lname).append(":"); if (!allowedMinSdk) { msg.append(" min-device-sdk=").append(minDeviceSdk); } if (!allowedMaxSdk) { msg.append(" max-device-sdk=").append(maxDeviceSdk); } if (!exists) { msg.append(" ").append(lfile).append(" does not exist"); } Slog.i(TAG, msg.toString()); } } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "feature": { if (allowFeatures) { String fname = parser.getAttributeValue(null, "name"); int fversion = XmlUtils.readIntAttribute(parser, "version", 0); boolean allowed; if (!lowRam) { allowed = true; } else { String notLowRam = parser.getAttributeValue(null, "notLowRam"); allowed = !"true".equals(notLowRam); } if (fname == null) { Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " + parser.getPositionDescription()); } else if (allowed) { addFeature(fname, fversion); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "unavailable-feature": { if (allowFeatures) { String fname = parser.getAttributeValue(null, "name"); if (fname == null) { Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " + parser.getPositionDescription()); } else { mUnavailableFeatures.add(fname); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "allow-in-power-save-except-idle": { if (allowOverrideAppRestrictions) { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mAllowInPowerSaveExceptIdle.add(pkgname); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "allow-in-power-save": { if (allowOverrideAppRestrictions) { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mAllowInPowerSave.add(pkgname); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "allow-in-data-usage-save": { if (allowOverrideAppRestrictions) { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mAllowInDataUsageSave.add(pkgname); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "allow-unthrottled-location": { if (allowOverrideAppRestrictions) { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mAllowUnthrottledLocation.add(pkgname); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "allow-adas-location-settings" : { if (allowOverrideAppRestrictions) { String pkgname = parser.getAttributeValue(null, "package"); String attributionTag = parser.getAttributeValue(null, "attributionTag"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { ArraySet tags = mAllowAdasSettings.get(pkgname); if (tags == null || !tags.isEmpty()) { if (tags == null) { tags = new ArraySet<>(1); mAllowAdasSettings.put(pkgname, tags); } if (!"*".equals(attributionTag)) { if ("null".equals(attributionTag)) { attributionTag = null; } tags.add(attributionTag); } } } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "allow-ignore-location-settings": { if (allowOverrideAppRestrictions) { String pkgname = parser.getAttributeValue(null, "package"); String attributionTag = parser.getAttributeValue(null, "attributionTag"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { ArraySet tags = mAllowIgnoreLocationSettings.get(pkgname); if (tags == null || !tags.isEmpty()) { if (tags == null) { tags = new ArraySet<>(1); mAllowIgnoreLocationSettings.put(pkgname, tags); } if (!"*".equals(attributionTag)) { if ("null".equals(attributionTag)) { attributionTag = null; } tags.add(attributionTag); } } } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "allow-implicit-broadcast": { if (allowImplicitBroadcasts) { String action = parser.getAttributeValue(null, "action"); if (action == null) { Slog.w(TAG, "<" + name + "> without action in " + permFile + " at " + parser.getPositionDescription()); } else { mAllowImplicitBroadcasts.add(action); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "app-link": { if (allowAppConfigs) { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mLinkedApps.add(pkgname); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "bg-restriction-exemption": { if (allowOverrideAppRestrictions) { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mBgRestrictionExemption.add(pkgname); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "default-enabled-vr-app": { if (allowAppConfigs) { String pkgname = parser.getAttributeValue(null, "package"); String clsname = parser.getAttributeValue(null, "class"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else if (clsname == null) { Slog.w(TAG, "<" + name + "> without class in " + permFile + " at " + parser.getPositionDescription()); } else { mDefaultVrComponents.add(new ComponentName(pkgname, clsname)); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "component-override": { readComponentOverrides(parser, permFile); } break; case "backup-transport-whitelisted-service": { if (allowFeatures) { String serviceName = parser.getAttributeValue(null, "service"); if (serviceName == null) { Slog.w(TAG, "<" + name + "> without service in " + permFile + " at " + parser.getPositionDescription()); } else { ComponentName cn = ComponentName.unflattenFromString(serviceName); if (cn == null) { Slog.w(TAG, "<" + name + "> with invalid service name " + serviceName + " in " + permFile + " at " + parser.getPositionDescription()); } else { mBackupTransportWhitelist.add(cn); } } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "disabled-until-used-preinstalled-carrier-associated-app": { if (allowAppConfigs) { String pkgname = parser.getAttributeValue(null, "package"); String carrierPkgname = parser.getAttributeValue(null, "carrierAppPackage"); if (pkgname == null || carrierPkgname == null) { Slog.w(TAG, "<" + name + "> without package or carrierAppPackage in " + permFile + " at " + parser.getPositionDescription()); } else { // APKs added to system images via OTA should specify the addedInSdk // attribute, otherwise they may be enabled-by-default in too many // cases. See CarrierAppUtils for more info. int addedInSdk = CarrierAssociatedAppEntry.SDK_UNSPECIFIED; String addedInSdkStr = parser.getAttributeValue(null, "addedInSdk"); if (!TextUtils.isEmpty(addedInSdkStr)) { try { addedInSdk = Integer.parseInt(addedInSdkStr); } catch (NumberFormatException e) { Slog.w(TAG, "<" + name + "> addedInSdk not an integer in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); break; } } List associatedPkgs = mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get( carrierPkgname); if (associatedPkgs == null) { associatedPkgs = new ArrayList<>(); mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put( carrierPkgname, associatedPkgs); } associatedPkgs.add( new CarrierAssociatedAppEntry(pkgname, addedInSdk)); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "disabled-until-used-preinstalled-carrier-app": { if (allowAppConfigs) { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without " + "package in " + permFile + " at " + parser.getPositionDescription()); } else { mDisabledUntilUsedPreinstalledCarrierApps.add(pkgname); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "privapp-permissions": { if (allowPrivappPermissions) { // privapp permissions from system, apex, vendor, product and // system_ext partitions are stored separately. This is to // prevent xml files in the vendor partition from granting // permissions to priv apps in the system partition and vice versa. boolean vendor = permFile.toPath().startsWith( Environment.getVendorDirectory().toPath() + "/") || permFile.toPath().startsWith( Environment.getOdmDirectory().toPath() + "/"); boolean product = permFile.toPath().startsWith( Environment.getProductDirectory().toPath() + "/"); boolean systemExt = permFile.toPath().startsWith( Environment.getSystemExtDirectory().toPath() + "/"); boolean apex = permFile.toPath().startsWith( Environment.getApexDirectory().toPath() + "/") && ApexProperties.updatable().orElse(false); if (vendor) { readPrivAppPermissions(parser, mVendorPrivAppPermissions, mVendorPrivAppDenyPermissions); } else if (product) { readPrivAppPermissions(parser, mProductPrivAppPermissions, mProductPrivAppDenyPermissions); } else if (systemExt) { readPrivAppPermissions(parser, mSystemExtPrivAppPermissions, mSystemExtPrivAppDenyPermissions); } else if (apex) { readApexPrivAppPermissions(parser, permFile, Environment.getApexDirectory().toPath()); } else { readPrivAppPermissions(parser, mPrivAppPermissions, mPrivAppDenyPermissions); } } else { logNotAllowedInPartition(name, permFile, parser); XmlUtils.skipCurrentTag(parser); } } break; case "oem-permissions": { if (allowOemPermissions) { readOemPermissions(parser); } else { logNotAllowedInPartition(name, permFile, parser); XmlUtils.skipCurrentTag(parser); } } break; case "hidden-api-whitelisted-app": { if (allowApiWhitelisting) { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mHiddenApiPackageWhitelist.add(pkgname); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "allow-association": { if (allowAssociations) { String target = parser.getAttributeValue(null, "target"); if (target == null) { Slog.w(TAG, "<" + name + "> without target in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); break; } String allowed = parser.getAttributeValue(null, "allowed"); if (allowed == null) { Slog.w(TAG, "<" + name + "> without allowed in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); break; } target = target.intern(); allowed = allowed.intern(); ArraySet associations = mAllowedAssociations.get(target); if (associations == null) { associations = new ArraySet<>(); mAllowedAssociations.put(target, associations); } Slog.i(TAG, "Adding association: " + target + " <- " + allowed); associations.add(allowed); } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "app-data-isolation-whitelisted-app": { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mAppDataIsolationWhitelistedApps.add(pkgname); } XmlUtils.skipCurrentTag(parser); } break; case "bugreport-whitelisted": { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mBugreportWhitelistedPackages.add(pkgname); } XmlUtils.skipCurrentTag(parser); } break; case "install-in-user-type": { // NB: We allow any directory permission to declare install-in-user-type. readInstallInUserType(parser, mPackageToUserTypeWhitelist, mPackageToUserTypeBlacklist); } break; case "named-actor": { String namespace = TextUtils.safeIntern( parser.getAttributeValue(null, "namespace")); String actorName = parser.getAttributeValue(null, "name"); String pkgName = TextUtils.safeIntern( parser.getAttributeValue(null, "package")); if (TextUtils.isEmpty(namespace)) { Slog.wtf(TAG, "<" + name + "> without namespace in " + permFile + " at " + parser.getPositionDescription()); } else if (TextUtils.isEmpty(actorName)) { Slog.wtf(TAG, "<" + name + "> without actor name in " + permFile + " at " + parser.getPositionDescription()); } else if (TextUtils.isEmpty(pkgName)) { Slog.wtf(TAG, "<" + name + "> without package name in " + permFile + " at " + parser.getPositionDescription()); } else if ("android".equalsIgnoreCase(namespace)) { throw new IllegalStateException("Defining " + actorName + " as " + pkgName + " for the android namespace is not allowed"); } else { if (mNamedActors == null) { mNamedActors = new ArrayMap<>(); } Map nameToPkgMap = mNamedActors.get(namespace); if (nameToPkgMap == null) { nameToPkgMap = new ArrayMap<>(); mNamedActors.put(namespace, nameToPkgMap); } else if (nameToPkgMap.containsKey(actorName)) { String existing = nameToPkgMap.get(actorName); throw new IllegalStateException("Duplicate actor definition for " + namespace + "/" + actorName + "; defined as both " + existing + " and " + pkgName); } nameToPkgMap.put(actorName, pkgName); } XmlUtils.skipCurrentTag(parser); } break; case "overlay-config-signature": { if (allowAll) { String pkgName = parser.getAttributeValue(null, "package"); if (pkgName == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { if (TextUtils.isEmpty(mOverlayConfigSignaturePackage)) { mOverlayConfigSignaturePackage = pkgName.intern(); } else { throw new IllegalStateException("Reference signature package " + "defined as both " + mOverlayConfigSignaturePackage + " and " + pkgName); } } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "rollback-whitelisted-app": { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mRollbackWhitelistedPackages.add(pkgname); } XmlUtils.skipCurrentTag(parser); } break; case "whitelisted-staged-installer": { if (allowAppConfigs) { String pkgname = parser.getAttributeValue(null, "package"); boolean isModulesInstaller = XmlUtils.readBooleanAttribute( parser, "isModulesInstaller", false); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mWhitelistedStagedInstallers.add(pkgname); } if (isModulesInstaller) { if (mModulesInstallerPackageName != null) { throw new IllegalStateException( "Multiple modules installers"); } mModulesInstallerPackageName = pkgname; } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; case "allowed-vendor-apex": { if (allowVendorApex) { String pkgName = parser.getAttributeValue(null, "package"); String installerPkgName = parser.getAttributeValue( null, "installerPackage"); if (pkgName == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } if (installerPkgName == null) { Slog.w(TAG, "<" + name + "> without installerPackage in " + permFile + " at " + parser.getPositionDescription()); } if (pkgName != null && installerPkgName != null) { mAllowedVendorApexes.put(pkgName, installerPkgName); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; default: { Slog.w(TAG, "Tag " + name + " is unknown in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); } break; } } } catch (XmlPullParserException e) { Slog.w(TAG, "Got exception parsing permissions.", e); } catch (IOException e) { Slog.w(TAG, "Got exception parsing permissions.", e); } finally { IoUtils.closeQuietly(permReader); } // Some devices can be field-converted to FBE, so offer to splice in // those features if not already defined by the static config if (StorageManager.isFileEncryptedNativeOnly()) { addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION, 0); addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0); } // Help legacy devices that may not have updated their static config if (StorageManager.hasAdoptable()) { addFeature(PackageManager.FEATURE_ADOPTABLE_STORAGE, 0); } if (ActivityManager.isLowRamDeviceStatic()) { addFeature(PackageManager.FEATURE_RAM_LOW, 0); } else { addFeature(PackageManager.FEATURE_RAM_NORMAL, 0); } final int incrementalVersion = IncrementalManager.getVersion(); if (incrementalVersion > 0) { addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, incrementalVersion); } if (PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT) { addFeature(PackageManager.FEATURE_APP_ENUMERATION, 0); } if (Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.Q) { addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0); } if (isErofsSupported()) { if (isKernelVersionAtLeast(5, 10)) { addFeature(PackageManager.FEATURE_EROFS, 0); } else if (isKernelVersionAtLeast(4, 19)) { addFeature(PackageManager.FEATURE_EROFS_LEGACY, 0); } } for (String featureName : mUnavailableFeatures) { removeFeature(featureName); } } private void addFeature(String name, int version) { FeatureInfo fi = mAvailableFeatures.get(name); if (fi == null) { fi = new FeatureInfo(); fi.name = name; fi.version = version; mAvailableFeatures.put(name, fi); } else { fi.version = Math.max(fi.version, version); } } private void removeFeature(String name) { if (mAvailableFeatures.remove(name) != null) { Slog.d(TAG, "Removed unavailable feature " + name); } } void readPermission(XmlPullParser parser, String name) throws IOException, XmlPullParserException { if (mPermissions.containsKey(name)) { throw new IllegalStateException("Duplicate permission definition for " + name); } final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false); final PermissionEntry perm = new PermissionEntry(name, perUser); mPermissions.put(name, perm); int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); if ("group".equals(tagName)) { String gidStr = parser.getAttributeValue(null, "gid"); if (gidStr != null) { int gid = Process.getGidForName(gidStr); perm.gids = appendInt(perm.gids, gid); } else { Slog.w(TAG, " without gid at " + parser.getPositionDescription()); } } XmlUtils.skipCurrentTag(parser); } } private void readPrivAppPermissions(XmlPullParser parser, ArrayMap> grantMap, ArrayMap> denyMap) throws IOException, XmlPullParserException { String packageName = parser.getAttributeValue(null, "package"); if (TextUtils.isEmpty(packageName)) { Slog.w(TAG, "package is required for in " + parser.getPositionDescription()); return; } ArraySet permissions = grantMap.get(packageName); if (permissions == null) { permissions = new ArraySet<>(); } ArraySet denyPermissions = denyMap.get(packageName); int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { String name = parser.getName(); if ("permission".equals(name)) { String permName = parser.getAttributeValue(null, "name"); if (TextUtils.isEmpty(permName)) { Slog.w(TAG, "name is required for in " + parser.getPositionDescription()); continue; } permissions.add(permName); } else if ("deny-permission".equals(name)) { String permName = parser.getAttributeValue(null, "name"); if (TextUtils.isEmpty(permName)) { Slog.w(TAG, "name is required for in " + parser.getPositionDescription()); continue; } if (denyPermissions == null) { denyPermissions = new ArraySet<>(); } denyPermissions.add(permName); } } grantMap.put(packageName, permissions); if (denyPermissions != null) { denyMap.put(packageName, denyPermissions); } } private void readInstallInUserType(XmlPullParser parser, Map> doInstallMap, Map> nonInstallMap) throws IOException, XmlPullParserException { final String packageName = parser.getAttributeValue(null, "package"); if (TextUtils.isEmpty(packageName)) { Slog.w(TAG, "package is required for in " + parser.getPositionDescription()); return; } Set userTypesYes = doInstallMap.get(packageName); Set userTypesNo = nonInstallMap.get(packageName); final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { final String name = parser.getName(); if ("install-in".equals(name)) { final String userType = parser.getAttributeValue(null, "user-type"); if (TextUtils.isEmpty(userType)) { Slog.w(TAG, "user-type is required for in " + parser.getPositionDescription()); continue; } if (userTypesYes == null) { userTypesYes = new ArraySet<>(); doInstallMap.put(packageName, userTypesYes); } userTypesYes.add(userType); } else if ("do-not-install-in".equals(name)) { final String userType = parser.getAttributeValue(null, "user-type"); if (TextUtils.isEmpty(userType)) { Slog.w(TAG, "user-type is required for in " + parser.getPositionDescription()); continue; } if (userTypesNo == null) { userTypesNo = new ArraySet<>(); nonInstallMap.put(packageName, userTypesNo); } userTypesNo.add(userType); } else { Slog.w(TAG, "unrecognized tag in in " + parser.getPositionDescription()); } } } void readOemPermissions(XmlPullParser parser) throws IOException, XmlPullParserException { final String packageName = parser.getAttributeValue(null, "package"); if (TextUtils.isEmpty(packageName)) { Slog.w(TAG, "package is required for in " + parser.getPositionDescription()); return; } ArrayMap permissions = mOemPermissions.get(packageName); if (permissions == null) { permissions = new ArrayMap<>(); } final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { final String name = parser.getName(); if ("permission".equals(name)) { final String permName = parser.getAttributeValue(null, "name"); if (TextUtils.isEmpty(permName)) { Slog.w(TAG, "name is required for in " + parser.getPositionDescription()); continue; } permissions.put(permName, Boolean.TRUE); } else if ("deny-permission".equals(name)) { String permName = parser.getAttributeValue(null, "name"); if (TextUtils.isEmpty(permName)) { Slog.w(TAG, "name is required for in " + parser.getPositionDescription()); continue; } permissions.put(permName, Boolean.FALSE); } } mOemPermissions.put(packageName, permissions); } private void readSplitPermission(XmlPullParser parser, File permFile) throws IOException, XmlPullParserException { String splitPerm = parser.getAttributeValue(null, "name"); if (splitPerm == null) { Slog.w(TAG, " without name in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); return; } String targetSdkStr = parser.getAttributeValue(null, "targetSdk"); int targetSdk = Build.VERSION_CODES.CUR_DEVELOPMENT + 1; if (!TextUtils.isEmpty(targetSdkStr)) { try { targetSdk = Integer.parseInt(targetSdkStr); } catch (NumberFormatException e) { Slog.w(TAG, " targetSdk not an integer in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); return; } } final int depth = parser.getDepth(); List newPermissions = new ArrayList<>(); while (XmlUtils.nextElementWithin(parser, depth)) { String name = parser.getName(); if ("new-permission".equals(name)) { final String newName = parser.getAttributeValue(null, "name"); if (TextUtils.isEmpty(newName)) { Slog.w(TAG, "name is required for in " + parser.getPositionDescription()); continue; } newPermissions.add(newName); } else { XmlUtils.skipCurrentTag(parser); } } if (!newPermissions.isEmpty()) { mSplitPermissions.add(new SplitPermissionInfo(splitPerm, newPermissions, targetSdk)); } } private void readComponentOverrides(XmlPullParser parser, File permFile) throws IOException, XmlPullParserException { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, " without package in " + permFile + " at " + parser.getPositionDescription()); return; } pkgname = pkgname.intern(); final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { if ("component".equals(parser.getName())) { String clsname = parser.getAttributeValue(null, "class"); String enabled = parser.getAttributeValue(null, "enabled"); if (clsname == null) { Slog.w(TAG, " without class in " + permFile + " at " + parser.getPositionDescription()); return; } else if (enabled == null) { Slog.w(TAG, " without enabled in " + permFile + " at " + parser.getPositionDescription()); return; } if (clsname.startsWith(".")) { clsname = pkgname + clsname; } clsname = clsname.intern(); ArrayMap componentEnabledStates = mPackageComponentEnabledState.get(pkgname); if (componentEnabledStates == null) { componentEnabledStates = new ArrayMap<>(); mPackageComponentEnabledState.put(pkgname, componentEnabledStates); } componentEnabledStates.put(clsname, !"false".equals(enabled)); } } } private void readPublicNativeLibrariesList() { readPublicLibrariesListFile(new File("/vendor/etc/public.libraries.txt")); String[] dirs = {"/system/etc", "/system_ext/etc", "/product/etc"}; for (String dir : dirs) { File[] files = new File(dir).listFiles(); if (files == null) { Slog.w(TAG, "Public libraries file folder missing: " + dir); continue; } for (File f : files) { String name = f.getName(); if (name.startsWith("public.libraries-") && name.endsWith(".txt")) { readPublicLibrariesListFile(f); } } } } private void readPublicLibrariesListFile(File listFile) { try (BufferedReader br = new BufferedReader(new FileReader(listFile))) { String line; while ((line = br.readLine()) != null) { if (line.isEmpty() || line.startsWith("#")) { continue; } // Line format is [abi]. We take the soname part. String soname = line.trim().split(" ")[0]; SharedLibraryEntry entry = new SharedLibraryEntry( soname, soname, new String[0], true); mSharedLibraries.put(entry.name, entry); } } catch (IOException e) { Slog.w(TAG, "Failed to read public libraries file " + listFile, e); } } /** * Returns the module name for a file in the apex module's partition. */ private String getApexModuleNameFromFilePath(Path path, Path apexDirectoryPath) { if (!path.startsWith(apexDirectoryPath)) { throw new IllegalArgumentException("File " + path + " is not part of an APEX."); } // File must be in //[extra_paths/] if (path.getNameCount() <= (apexDirectoryPath.getNameCount() + 1)) { throw new IllegalArgumentException("File " + path + " is in the APEX partition," + " but not inside a module."); } return path.getName(apexDirectoryPath.getNameCount()).toString(); } /** * Reads the contents of the privileged permission allowlist stored inside an APEX. */ @VisibleForTesting public void readApexPrivAppPermissions(XmlPullParser parser, File permFile, Path apexDirectoryPath) throws IOException, XmlPullParserException { final String moduleName = getApexModuleNameFromFilePath(permFile.toPath(), apexDirectoryPath); final ArrayMap> privAppPermissions; if (mApexPrivAppPermissions.containsKey(moduleName)) { privAppPermissions = mApexPrivAppPermissions.get(moduleName); } else { privAppPermissions = new ArrayMap<>(); mApexPrivAppPermissions.put(moduleName, privAppPermissions); } final ArrayMap> privAppDenyPermissions; if (mApexPrivAppDenyPermissions.containsKey(moduleName)) { privAppDenyPermissions = mApexPrivAppDenyPermissions.get(moduleName); } else { privAppDenyPermissions = new ArrayMap<>(); mApexPrivAppDenyPermissions.put(moduleName, privAppDenyPermissions); } readPrivAppPermissions(parser, privAppPermissions, privAppDenyPermissions); } private static boolean isSystemProcess() { return Process.myUid() == Process.SYSTEM_UID; } private static boolean isErofsSupported() { try { final Path path = Paths.get("/sys/fs/erofs"); return Files.exists(path); } catch (Exception e) { return false; } } private static boolean isKernelVersionAtLeast(int major, int minor) { final String kernelVersion = VintfRuntimeInfo.getKernelVersion(); final String[] parts = kernelVersion.split("\\."); if (parts.length < 2) { return false; } try { final int majorVersion = Integer.parseInt(parts[0]); final int minorVersion = Integer.parseInt(parts[1]); return majorVersion > major || (majorVersion == major && minorVersion >= minor); } catch (NumberFormatException e) { return false; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy