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

Commit 5d201fd4 authored by Mark Punzalan's avatar Mark Punzalan Committed by Gerrit Code Review
Browse files

Merge "[aapt2] Use FeatureFlagsFilter in link command" into main

parents 71652683 4b564ded
Loading
Loading
Loading
Loading
+15 −1
Original line number Original line Diff line number Diff line
@@ -56,6 +56,7 @@
#include "java/JavaClassGenerator.h"
#include "java/JavaClassGenerator.h"
#include "java/ManifestClassGenerator.h"
#include "java/ManifestClassGenerator.h"
#include "java/ProguardRules.h"
#include "java/ProguardRules.h"
#include "link/FeatureFlagsFilter.h"
#include "link/Linkers.h"
#include "link/Linkers.h"
#include "link/ManifestFixer.h"
#include "link/ManifestFixer.h"
#include "link/NoDefaultResourceRemover.h"
#include "link/NoDefaultResourceRemover.h"
@@ -1986,6 +1987,19 @@ class Linker {
    context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
    context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
    context_->SetSplitNameDependencies(app_info_.split_name_dependencies);
    context_->SetSplitNameDependencies(app_info_.split_name_dependencies);


    FeatureFlagsFilterOptions flags_filter_options;
    if (context_->GetMinSdkVersion() > SDK_UPSIDE_DOWN_CAKE) {
      // For API version > U, PackageManager will dynamically read the flag values and disable
      // manifest elements accordingly when parsing the manifest.
      // For API version <= U, we remove disabled elements from the manifest with the filter.
      flags_filter_options.remove_disabled_elements = false;
      flags_filter_options.flags_must_have_value = false;
    }
    FeatureFlagsFilter flags_filter(options_.feature_flag_values, flags_filter_options);
    if (!flags_filter.Consume(context_, manifest_xml.get())) {
      return 1;
    }

    // Override the package ID when it is "android".
    // Override the package ID when it is "android".
    if (context_->GetCompilationPackage() == "android") {
    if (context_->GetCompilationPackage() == "android") {
      context_->SetPackageId(kAndroidPackageId);
      context_->SetPackageId(kAndroidPackageId);
@@ -2530,7 +2544,7 @@ int LinkCommand::Action(const std::vector<std::string>& args) {
  }
  }


  for (const std::string& arg : all_feature_flags_args) {
  for (const std::string& arg : all_feature_flags_args) {
    if (ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) {
    if (!ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) {
      return 1;
      return 1;
    }
    }
  }
  }
+5 −1
Original line number Original line Diff line number Diff line
@@ -330,7 +330,11 @@ class LinkCommand : public Command {
            "should only be used together with the --static-lib flag.",
            "should only be used together with the --static-lib flag.",
        &options_.merge_only);
        &options_.merge_only);
    AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
    AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
    AddOptionalFlagList("--feature-flags", "Placeholder, to be implemented.", &feature_flags_args_);
    AddOptionalFlagList("--feature-flags",
                        "Specify the values of feature flags. The pairs in the argument\n"
                        "are separated by ',' and the name is separated from the value by '='.\n"
                        "Example: \"flag1=true,flag2=false,flag3=\" (flag3 has no given value).",
                        &feature_flags_args_);
  }
  }


  int Action(const std::vector<std::string>& args) override;
  int Action(const std::vector<std::string>& args) override;
+211 −3
Original line number Original line Diff line number Diff line
@@ -16,11 +16,10 @@


#include "Link.h"
#include "Link.h"


#include <android-base/file.h>

#include "AppInfo.h"
#include "Diagnostics.h"
#include "Diagnostics.h"
#include "LoadedApk.h"
#include "LoadedApk.h"
#include "android-base/file.h"
#include "android-base/stringprintf.h"
#include "test/Test.h"
#include "test/Test.h"


using testing::Eq;
using testing::Eq;
@@ -993,4 +992,213 @@ TEST_F(LinkTest, LocaleConfigWrongLocaleFormat) {
  ASSERT_FALSE(Link(link_args, &diag));
  ASSERT_FALSE(Link(link_args, &diag));
}
}


static void BuildSDKWithFeatureFlagAttr(const std::string& apk_path, const std::string& java_path,
                                        CommandTestFixture* fixture, android::IDiagnostics* diag) {
  const std::string android_values =
      R"(<resources>
          <staging-public-group type="attr" first-id="0x01fe0063">
            <public name="featureFlag" />
          </staging-public-group>
          <attr name="featureFlag" format="string" />
         </resources>)";

  SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
  BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
}

TEST_F(LinkTest, FeatureFlagDisabled_SdkAtMostUDC) {
  StdErrDiagnostics diag;
  const std::string android_apk = GetTestPath("android.apk");
  const std::string android_java = GetTestPath("android-java");
  BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);

  const std::string manifest_contents = android::base::StringPrintf(
      R"(<uses-sdk android:minSdkVersion="%d" />"
          <permission android:name="FOO" android:featureFlag="flag" />)",
      SDK_UPSIDE_DOWN_CAKE);
  auto app_manifest = ManifestBuilder(this)
                          .SetPackageName("com.example.app")
                          .AddContents(manifest_contents)
                          .Build();

  auto app_link_args = LinkCommandBuilder(this)
                           .SetManifestFile(app_manifest)
                           .AddParameter("-I", android_apk)
                           .AddParameter("--feature-flags", "flag=false");

  const std::string app_apk = GetTestPath("app.apk");
  BuildApk({}, app_apk, std::move(app_link_args), this, &diag);

  // Permission element should be removed if flag is disabled
  auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
  ASSERT_THAT(apk, NotNull());
  auto apk_manifest = apk->GetManifest();
  ASSERT_THAT(apk_manifest, NotNull());
  auto root = apk_manifest->root.get();
  ASSERT_THAT(root, NotNull());
  auto maybe_removed = root->FindChild({}, "permission");
  ASSERT_THAT(maybe_removed, IsNull());
}

TEST_F(LinkTest, FeatureFlagEnabled_SdkAtMostUDC) {
  StdErrDiagnostics diag;
  const std::string android_apk = GetTestPath("android.apk");
  const std::string android_java = GetTestPath("android-java");
  BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);

  const std::string manifest_contents = android::base::StringPrintf(
      R"(<uses-sdk android:minSdkVersion="%d" />"
          <permission android:name="FOO" android:featureFlag="flag" />)",
      SDK_UPSIDE_DOWN_CAKE);
  auto app_manifest = ManifestBuilder(this)
                          .SetPackageName("com.example.app")
                          .AddContents(manifest_contents)
                          .Build();

  auto app_link_args = LinkCommandBuilder(this)
                           .SetManifestFile(app_manifest)
                           .AddParameter("-I", android_apk)
                           .AddParameter("--feature-flags", "flag=true");

  const std::string app_apk = GetTestPath("app.apk");
  BuildApk({}, app_apk, std::move(app_link_args), this, &diag);

  // Permission element should be kept if flag is enabled
  auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
  ASSERT_THAT(apk, NotNull());
  auto apk_manifest = apk->GetManifest();
  ASSERT_THAT(apk_manifest, NotNull());
  auto root = apk_manifest->root.get();
  ASSERT_THAT(root, NotNull());
  auto maybe_removed = root->FindChild({}, "permission");
  ASSERT_THAT(maybe_removed, NotNull());
}

TEST_F(LinkTest, FeatureFlagWithNoValue_SdkAtMostUDC) {
  StdErrDiagnostics diag;
  const std::string android_apk = GetTestPath("android.apk");
  const std::string android_java = GetTestPath("android-java");
  BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);

  const std::string manifest_contents = android::base::StringPrintf(
      R"(<uses-sdk android:minSdkVersion="%d" />"
          <permission android:name="FOO" android:featureFlag="flag" />)",
      SDK_UPSIDE_DOWN_CAKE);
  auto app_manifest = ManifestBuilder(this)
                          .SetPackageName("com.example.app")
                          .AddContents(manifest_contents)
                          .Build();

  auto app_link_args = LinkCommandBuilder(this)
                           .SetManifestFile(app_manifest)
                           .AddParameter("-I", android_apk)
                           .AddParameter("--feature-flags", "flag=");

  // Flags must have values if <= UDC
  const std::string app_apk = GetTestPath("app.apk");
  ASSERT_FALSE(Link(app_link_args.Build(app_apk), &diag));
}

TEST_F(LinkTest, FeatureFlagDisabled_SdkAfterUDC) {
  StdErrDiagnostics diag;
  const std::string android_apk = GetTestPath("android.apk");
  const std::string android_java = GetTestPath("android-java");
  BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);

  const std::string manifest_contents = android::base::StringPrintf(
      R"(<uses-sdk android:minSdkVersion="%d" />"
          <permission android:name="FOO" android:featureFlag="flag" />)",
      SDK_CUR_DEVELOPMENT);
  auto app_manifest = ManifestBuilder(this)
                          .SetPackageName("com.example.app")
                          .AddContents(manifest_contents)
                          .Build();

  auto app_link_args = LinkCommandBuilder(this)
                           .SetManifestFile(app_manifest)
                           .AddParameter("-I", android_apk)
                           .AddParameter("--feature-flags", "flag=false");

  const std::string app_apk = GetTestPath("app.apk");
  BuildApk({}, app_apk, std::move(app_link_args), this, &diag);

  // Permission element should be kept if > UDC, regardless of flag value
  auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
  ASSERT_THAT(apk, NotNull());
  auto apk_manifest = apk->GetManifest();
  ASSERT_THAT(apk_manifest, NotNull());
  auto root = apk_manifest->root.get();
  ASSERT_THAT(root, NotNull());
  auto maybe_removed = root->FindChild({}, "permission");
  ASSERT_THAT(maybe_removed, NotNull());
}

TEST_F(LinkTest, FeatureFlagEnabled_SdkAfterUDC) {
  StdErrDiagnostics diag;
  const std::string android_apk = GetTestPath("android.apk");
  const std::string android_java = GetTestPath("android-java");
  BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);

  const std::string manifest_contents = android::base::StringPrintf(
      R"(<uses-sdk android:minSdkVersion="%d" />"
          <permission android:name="FOO" android:featureFlag="flag" />)",
      SDK_CUR_DEVELOPMENT);
  auto app_manifest = ManifestBuilder(this)
                          .SetPackageName("com.example.app")
                          .AddContents(manifest_contents)
                          .Build();

  auto app_link_args = LinkCommandBuilder(this)
                           .SetManifestFile(app_manifest)
                           .AddParameter("-I", android_apk)
                           .AddParameter("--feature-flags", "flag=true");

  const std::string app_apk = GetTestPath("app.apk");
  BuildApk({}, app_apk, std::move(app_link_args), this, &diag);

  // Permission element should be kept if > UDC, regardless of flag value
  auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
  ASSERT_THAT(apk, NotNull());
  auto apk_manifest = apk->GetManifest();
  ASSERT_THAT(apk_manifest, NotNull());
  auto root = apk_manifest->root.get();
  ASSERT_THAT(root, NotNull());
  auto maybe_removed = root->FindChild({}, "permission");
  ASSERT_THAT(maybe_removed, NotNull());
}

TEST_F(LinkTest, FeatureFlagWithNoValue_SdkAfterUDC) {
  StdErrDiagnostics diag;
  const std::string android_apk = GetTestPath("android.apk");
  const std::string android_java = GetTestPath("android-java");
  BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);

  const std::string manifest_contents = android::base::StringPrintf(
      R"(<uses-sdk android:minSdkVersion="%d" />"
          <permission android:name="FOO" android:featureFlag="flag" />)",
      SDK_CUR_DEVELOPMENT);
  auto app_manifest = ManifestBuilder(this)
                          .SetPackageName("com.example.app")
                          .AddContents(manifest_contents)
                          .Build();

  auto app_link_args = LinkCommandBuilder(this)
                           .SetManifestFile(app_manifest)
                           .AddParameter("-I", android_apk)
                           .AddParameter("--feature-flags", "flag=");

  const std::string app_apk = GetTestPath("app.apk");
  BuildApk({}, app_apk, std::move(app_link_args), this, &diag);

  // Permission element should be kept if > UDC, regardless of flag value
  auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
  ASSERT_THAT(apk, NotNull());
  auto apk_manifest = apk->GetManifest();
  ASSERT_THAT(apk_manifest, NotNull());
  auto root = apk_manifest->root.get();
  ASSERT_THAT(root, NotNull());
  auto maybe_removed = root->FindChild({}, "permission");
  ASSERT_THAT(maybe_removed, NotNull());
}

}  // namespace aapt
}  // namespace aapt