Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b93942a authored by Ryan Mitchell's avatar Ryan Mitchell
Browse files

Add xml configuration of RROs

This change adds the ability to configure the priority, default enable
state, and mutability (previously know as staticness) of an overlay.
Rather than overlays configuring themselves, the system can configure
overlays relative to each other.

An example configuration file looks like:
<config>
    <merge path="auto-generated.xml" />
    <overlay package="com.example.one" mutable="false"
             enabled="true"/>
    <overlay package="com.example.two" mutable="false"
             enabled="true"/>
    <overlay package="com.example.three" enabled="true"/>
</config>

The <overlay> tag configures the overlay while the <merge> tag allows
additional configuration files to be included at a position within
the configuration file.

If the configuration file is not present for a partition, the legacy
android:isStatic and android:priority will continue to configure the
overlays in the partition. Once at least one configuration file has
been defined in any partition, strict partition precedence will be
enforced and overlays on separate partitions will no longer be able
to use android:priority to reorder themselves conversely from the
overlay partition precedence.

The order of the system partitions from least to greatest precedence
is system, vendor, odm, oem, product, system_ext.

Bug: 135048762
Test: atest OverlayConfigTest
Change-Id: If57e8caa9b881f9d424ef48bba80b18cc8b7b943
parent 76e66906
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -146,6 +146,7 @@ cc_binary {
    host_supported: true,
    srcs: [
        "idmap2/Create.cpp",
        "idmap2/CreateMultiple.cpp",
        "idmap2/Dump.cpp",
        "idmap2/Lookup.cpp",
        "idmap2/Main.cpp",
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include "idmap2/Result.h"

android::idmap2::Result<android::idmap2::Unit> Create(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> CreateMultiple(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Dump(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Lookup(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Scan(const std::vector<std::string>& args);
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */

#include <sys/stat.h>   // umask
#include <sys/types.h>  // umask

#include <fstream>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>

#include "android-base/stringprintf.h"
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
#include "idmap2/Policies.h"
#include "idmap2/SysTrace.h"

using android::ApkAssets;
using android::base::StringPrintf;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
using android::idmap2::PoliciesToBitmask;
using android::idmap2::PolicyBitmask;
using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
using android::idmap2::utils::UidHasWriteAccessToPath;

Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
  SYSTRACE << "CreateMultiple " << args;
  std::string target_apk_path;
  std::string idmap_dir = kIdmapCacheDir;
  std::vector<std::string> overlay_apk_paths;
  std::vector<std::string> policies;
  bool ignore_overlayable = false;

  const CommandLineOptions opts =
      CommandLineOptions("idmap2 create-multiple")
          .MandatoryOption("--target-apk-path",
                           "input: path to apk which will have its resources overlaid",
                           &target_apk_path)
          .MandatoryOption("--overlay-apk-path",
                           "input: path to apk which contains the new resource values",
                           &overlay_apk_paths)
          .OptionalOption("--idmap-dir",
                          StringPrintf("output: path to the directory in which to write idmap file"
                                       " (defaults to %s)",
                                       kIdmapCacheDir),
                          &idmap_dir)
          .OptionalOption("--policy",
                          "input: an overlayable policy this overlay fulfills"
                          " (if none or supplied, the overlay policy will default to \"public\")",
                          &policies)
          .OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks",
                        &ignore_overlayable);
  const auto opts_ok = opts.Parse(args);
  if (!opts_ok) {
    return opts_ok.GetError();
  }

  PolicyBitmask fulfilled_policies = 0;
  auto conv_result = PoliciesToBitmask(policies);
  if (conv_result) {
    fulfilled_policies |= *conv_result;
  } else {
    return conv_result.GetError();
  }

  if (fulfilled_policies == 0) {
    fulfilled_policies |= PolicyFlags::POLICY_PUBLIC;
  }

  const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
  if (!target_apk) {
    return Error("failed to load apk %s", target_apk_path.c_str());
  }

  std::vector<std::string> idmap_paths;
  for (const std::string& overlay_apk_path : overlay_apk_paths) {
    const std::string idmap_path = Idmap::CanonicalIdmapPathFor(idmap_dir, overlay_apk_path);
    const uid_t uid = getuid();
    if (!UidHasWriteAccessToPath(uid, idmap_path)) {
      LOG(WARNING) << "uid " << uid << "does not have write access to " << idmap_path.c_str();
      continue;
    }

    const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
    if (!overlay_apk) {
      LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
      continue;
    }

    const auto idmap =
        Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
    if (!idmap) {
      LOG(WARNING) << "failed to create idmap";
      continue;
    }

    umask(kIdmapFilePermissionMask);
    std::ofstream fout(idmap_path);
    if (fout.fail()) {
      LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str();
      continue;
    }

    BinaryStreamVisitor visitor(fout);
    (*idmap)->accept(&visitor);
    fout.close();
    if (fout.fail()) {
      LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str();
      continue;
    }

    idmap_paths.emplace_back(idmap_path);
  }

  for (const std::string& idmap_path : idmap_paths) {
    std::cout << idmap_path << std::endl;
  }

  return Unit{};
}
+3 −1
Original line number Diff line number Diff line
@@ -53,7 +53,9 @@ void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) {
int main(int argc, char** argv) {
  SYSTRACE << "main";
  const NameToFunctionMap commands = {
      {"create", Create}, {"dump", Dump}, {"lookup", Lookup}, {"scan", Scan}, {"verify", Verify},
      {"create", Create}, {"create-multiple", CreateMultiple},
      {"dump", Dump},     {"lookup", Lookup},
      {"scan", Scan},     {"verify", Verify},
  };
  if (argc <= 1) {
    PrintUsage(commands, std::cerr);
+46 −4
Original line number Diff line number Diff line
@@ -484,6 +484,9 @@ public class PackageParser {
        public final boolean isolatedSplits;
        public final boolean isSplitRequired;
        public final boolean useEmbeddedDex;
        public final String targetPackageName;
        public final boolean overlayIsStatic;
        public final int overlayPriority;

        public ApkLite(String codePath, String packageName, String splitName,
                boolean isFeatureSplit,
@@ -493,6 +496,7 @@ public class PackageParser {
                SigningDetails signingDetails, boolean coreApp,
                boolean debuggable, boolean multiArch, boolean use32bitAbi,
                boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
                String targetPackageName, boolean overlayIsStatic, int overlayPriority,
                int minSdkVersion, int targetSdkVersion) {
            this.codePath = codePath;
            this.packageName = packageName;
@@ -514,6 +518,9 @@ public class PackageParser {
            this.extractNativeLibs = extractNativeLibs;
            this.isolatedSplits = isolatedSplits;
            this.isSplitRequired = isSplitRequired;
            this.targetPackageName = targetPackageName;
            this.overlayIsStatic = overlayIsStatic;
            this.overlayPriority = overlayPriority;
            this.minSdkVersion = minSdkVersion;
            this.targetSdkVersion = targetSdkVersion;
        }
@@ -1797,6 +1804,12 @@ public class PackageParser {
        boolean useEmbeddedDex = false;
        String configForSplit = null;
        String usesSplitName = null;
        String targetPackage = null;
        boolean overlayIsStatic = false;
        int overlayPriority = 0;

        String requiredSystemPropertyName = null;
        String requiredSystemPropertyValue = null;

        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            final String attr = attrs.getAttributeName(i);
@@ -1861,6 +1874,21 @@ public class PackageParser {
                        useEmbeddedDex = attrs.getAttributeBooleanValue(i, false);
                    }
                }
            } else if (PackageParser.TAG_OVERLAY.equals(parser.getName())) {
                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                    final String attr = attrs.getAttributeName(i);
                    if ("requiredSystemPropertyName".equals(attr)) {
                        requiredSystemPropertyName = attrs.getAttributeValue(i);
                    } else if ("requiredSystemPropertyValue".equals(attr)) {
                        requiredSystemPropertyValue = attrs.getAttributeValue(i);
                    } else if ("targetPackage".equals(attr)) {
                        targetPackage = attrs.getAttributeValue(i);;
                    } else if ("isStatic".equals(attr)) {
                        overlayIsStatic = attrs.getAttributeBooleanValue(i, false);
                    } else if ("priority".equals(attr)) {
                        overlayPriority = attrs.getAttributeIntValue(i, 0);
                    }
                }
            } else if (TAG_USES_SPLIT.equals(parser.getName())) {
                if (usesSplitName != null) {
                    Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
@@ -1887,11 +1915,22 @@ public class PackageParser {
            }
        }

        // Check to see if overlay should be excluded based on system property condition
        if (!checkRequiredSystemProperty(requiredSystemPropertyName,
                requiredSystemPropertyValue)) {
            Slog.i(TAG, "Skipping target and overlay pair " + targetPackage + " and "
                    + codePath + ": overlay ignored due to required system property: "
                    + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue);
            targetPackage = null;
            overlayIsStatic = false;
            overlayPriority = 0;
        }

        return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
                configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
                revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
                multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits,
                minSdkVersion, targetSdkVersion);
                targetPackage, overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion);
    }

    /**
@@ -2175,7 +2214,7 @@ public class PackageParser {
                }

                // check to see if overlay should be excluded based on system property condition
                if (!checkOverlayRequiredSystemProperty(propName, propValue)) {
                if (!checkRequiredSystemProperty(propName, propValue)) {
                    Slog.i(TAG, "Skipping target and overlay pair " + pkg.mOverlayTarget + " and "
                        + pkg.baseCodePath+ ": overlay ignored due to required system property: "
                        + propName + " with value: " + propValue);
@@ -2603,8 +2642,11 @@ public class PackageParser {
        return pkg;
    }

    private boolean checkOverlayRequiredSystemProperty(String propName, String propValue) {

    /**
     * Returns {@code true} if both the property name and value are empty or if the given system
     * property is set to the specified value. In all other cases, returns {@code false}
     */
    public static boolean checkRequiredSystemProperty(String propName, String propValue) {
        if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) {
            if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) {
                // malformed condition - incomplete
Loading